From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 121 ++++++++++++++++++++- dlls/uiautomationcore/uia_client.c | 38 ++++++- 2 files changed, 156 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 95e4de1b106..715629cd8df 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1140,6 +1140,8 @@ static struct Provider } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); +static void set_provider_prop_override(struct Provider *prov, struct Provider_prop_override *override, int count); +static void set_property_override(struct Provider_prop_override *override, int prop_id, VARIANT *val);
static const WCHAR *uia_bstr_prop_str = L"uia-string"; static const ULONG uia_i4_prop_val = 0xdeadbeef; @@ -7577,26 +7579,38 @@ static const struct prov_method_sequence cache_req_seq6[] = { { 0 } };
+static const struct prov_method_sequence cache_req_seq7[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId. */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId. */ + { 0 } +}; + static const struct UiaCondition UiaTrueCondition = { ConditionType_True }; static const struct UiaCondition UiaFalseCondition = { ConditionType_False }; static void test_UiaGetUpdatedCache(void) { + LONG exp_lbound[2], exp_elems[2], idx[2], i; struct Provider_prop_override prop_override; struct node_provider_desc exp_node_desc[2]; struct UiaPropertyCondition prop_cond; struct UiaAndOrCondition and_or_cond; - LONG exp_lbound[2], exp_elems[2], i; struct UiaCacheRequest cache_req; struct UiaCondition *cond_arr[2]; struct UiaNotCondition not_cond; + VARIANT v, v_arr[2]; SAFEARRAY *out_req; + IUnknown *unk_ns; BSTR tree_struct; + int prop_ids[2]; HUIANODE node; HRESULT hr; - VARIANT v;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ hr = UiaGetReservedNotSupportedValue(&unk_ns); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL);
@@ -8018,9 +8032,112 @@ static void test_UiaGetUpdatedCache(void) Provider.prop_override = NULL; Provider.prop_override_count = 0;
+ /* + * Tests for property value caching. + */ + prop_ids[0] = UIA_RuntimeIdPropertyId; + /* Invalid property ID, no work will be done. */ + prop_ids[1] = 1; + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, NULL, TreeScope_Element, prop_ids, ARRAY_SIZE(prop_ids), NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + ok(!tree_struct, "tree_struct != NULL\n"); + + /* + * Retrieve values for UIA_RuntimeIdPropertyId and + * UIA_IsControlElementPropertyId in the returned cache. + */ + prop_ids[0] = UIA_RuntimeIdPropertyId; + prop_ids[1] = UIA_IsControlElementPropertyId; + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, NULL, TreeScope_Element, prop_ids, ARRAY_SIZE(prop_ids), NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 3; + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + ok(!wcscmp(tree_struct, L"P)"), "tree structure %s\n", debugstr_w(tree_struct)); + + idx[0] = 0; + for (i = 0; i < ARRAY_SIZE(prop_ids); i++) + { + idx[1] = 1 + i; + VariantInit(&v_arr[i]); + hr = SafeArrayGetElement(out_req, idx, &v_arr[i]); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + } + + ok(V_VT(&v_arr[0]) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v_arr[0]) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v_arr[0])); + VariantClear(&v_arr[0]); + + ok(check_variant_bool(&v_arr[1], TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v_arr[1])); + VariantClear(&v_arr[1]); + + ok_method_sequence(cache_req_seq7, "cache_req_seq7"); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + /* + * Again, but return a valid runtime ID and a different value for + * UIA_IsControlElementPropertyId. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, NULL, TreeScope_Element, prop_ids, ARRAY_SIZE(prop_ids), NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 3; + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + ok(!wcscmp(tree_struct, L"P)"), "tree structure %s\n", debugstr_w(tree_struct)); + + idx[0] = 0; + for (i = 0; i < ARRAY_SIZE(prop_ids); i++) + { + idx[1] = 1 + i; + VariantInit(&v_arr[i]); + hr = SafeArrayGetElement(out_req, idx, &v_arr[i]); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + } + + ok(V_VT(&v_arr[0]) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v_arr[0])); + check_runtime_id(Provider.runtime_id, ARRAY_SIZE(Provider.runtime_id), V_ARRAY(&v_arr[0])); + VariantClear(&v_arr[0]); + + ok(check_variant_bool(&v_arr[1], FALSE), "V_BOOL(&v) = %#x\n", V_BOOL(&v_arr[1])); + VariantClear(&v_arr[1]); + + ok_method_sequence(cache_req_seq7, "cache_req_seq7"); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE);
+ IUnknown_Release(unk_ns); CoUninitialize(); }
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 8d543bd4dc7..4de9dc1919e 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2787,6 +2787,7 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac LONG idx[2]; HRESULT hr; VARIANT v; + int i;
TRACE("(%p, %p, %u, %p, %p, %p)\n", huianode, cache_req, normalize_state, normalize_cond, out_req, tree_struct);
@@ -2802,6 +2803,18 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac return E_NOTIMPL; }
+ if (cache_req->cPatterns && cache_req->pPatterns) + FIXME("Pattern caching currently unimplemented\n"); + + if (cache_req->cProperties && cache_req->pProperties) + { + for (i = 0; i < cache_req->cProperties; i++) + { + if (!uia_prop_info_from_id(cache_req->pProperties[i])) + return E_INVALIDARG; + } + } + switch (normalize_state) { case NormalizeState_None: @@ -2834,7 +2847,8 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac } }
- sabound[0].cElements = sabound[1].cElements = 1; + sabound[0].cElements = 1; + sabound[1].cElements = 1 + cache_req->cProperties; sabound[0].lLbound = sabound[1].lLbound = 0; if (!(sa = SafeArrayCreate(VT_VARIANT, 2, sabound))) { @@ -2852,6 +2866,28 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac return hr; }
+ idx[0] = 0; + VariantClear(&v); + for (i = 0; i < cache_req->cProperties; i++) + { + hr = UiaGetPropertyValue(huianode, cache_req->pProperties[i], &v); + /* Don't fail on unimplemented properties. */ + if (FAILED(hr) && hr != E_NOTIMPL) + { + SafeArrayDestroy(sa); + return hr; + } + + idx[1] = 1 + i; + hr = SafeArrayPutElement(sa, idx, &v); + VariantClear(&v); + if (FAILED(hr)) + { + SafeArrayDestroy(sa); + return hr; + } + } + /* * AddRef huianode since we're returning a reference to the same node we * passed in, rather than creating a new one.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 18 +++++++-- dlls/uiautomationcore/uia_client.c | 31 ---------------- dlls/uiautomationcore/uia_com_client.c | 43 ++++++++++++++++++++-- dlls/uiautomationcore/uia_main.c | 1 - dlls/uiautomationcore/uia_private.h | 31 ++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 1 - 6 files changed, 85 insertions(+), 40 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 715629cd8df..41bd168e3c6 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10839,6 +10839,16 @@ static void test_CUIAutomation_cache_request_iface(IUIAutomation *uia_iface) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(elem_mode == AutomationElementMode_None, "Unexpected element mode %#x\n", elem_mode);
+ /* + * AddProperty tests. + */ + hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_IsContentElementPropertyId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Invalid property ID. */ + hr = IUIAutomationCacheRequest_AddProperty(cache_req, 1); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + IUIAutomationCacheRequest_Release(cache_req); }
@@ -10849,13 +10859,13 @@ static const struct prov_method_sequence get_elem_cache_seq[] = { };
static const struct prov_method_sequence get_cached_prop_val_seq[] = { - { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, + { &Provider_child, FRAG_GET_RUNTIME_ID }, { 0 }, };
static const struct prov_method_sequence get_cached_prop_val_seq2[] = { - { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, - { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ { 0 }, };
@@ -10963,7 +10973,7 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) * values. */ hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_IsControlElementPropertyId); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadb33f; element2 = NULL; diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 4de9dc1919e..195974c25f6 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -19,42 +19,11 @@ #include "uia_private.h"
#include "wine/debug.h" -#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
static const struct UiaCondition UiaFalseCondition = { ConditionType_False };
-static BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) -{ - SIZE_T max_capacity, new_capacity; - void *new_elements; - - if (count <= *capacity) - return TRUE; - - max_capacity = ~(SIZE_T)0 / size; - if (count > max_capacity) - return FALSE; - - new_capacity = max(1, *capacity); - while (new_capacity < count && new_capacity <= max_capacity / 2) - new_capacity *= 2; - if (new_capacity < count) - new_capacity = count; - - if (!*elements) - new_elements = heap_alloc_zero(new_capacity * size); - else - new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); - if (!new_elements) - return FALSE; - - *elements = new_elements; - *capacity = new_capacity; - return TRUE; -} - struct uia_node_array { HUIANODE *nodes; int node_count; diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 44d715684c1..2b499fa8bc5 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -19,7 +19,6 @@ #include "uia_private.h"
#include "wine/debug.h" -#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
@@ -632,6 +631,10 @@ struct uia_cache_request {
IUIAutomationCondition *view_condition; struct UiaCacheRequest cache_req; + + int *prop_ids; + int prop_ids_count; + SIZE_T prop_ids_arr_size; };
static inline struct uia_cache_request *impl_from_IUIAutomationCacheRequest(IUIAutomationCacheRequest *iface) @@ -670,6 +673,7 @@ static ULONG WINAPI uia_cache_request_Release(IUIAutomationCacheRequest *iface) if (!ref) { IUIAutomationCondition_Release(uia_cache_request->view_condition); + heap_free(uia_cache_request->prop_ids); heap_free(uia_cache_request); }
@@ -678,8 +682,33 @@ static ULONG WINAPI uia_cache_request_Release(IUIAutomationCacheRequest *iface)
static HRESULT WINAPI uia_cache_request_AddProperty(IUIAutomationCacheRequest *iface, PROPERTYID prop_id) { - FIXME("%p, %d: stub\n", iface, prop_id); - return E_NOTIMPL; + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + const struct uia_prop_info *prop_info = uia_prop_info_from_id(prop_id); + int i; + + TRACE("%p, %d\n", iface, prop_id); + + if (!prop_info) + return E_INVALIDARG; + + /* Don't add a duplicate property to the cache request. */ + for (i = 0; i < uia_cache_request->prop_ids_count; i++) + { + if (uia_cache_request->prop_ids[i] == prop_id) + return S_OK; + } + + if (!uia_array_reserve((void **)&uia_cache_request->prop_ids, &uia_cache_request->prop_ids_arr_size, + uia_cache_request->prop_ids_count + 1, sizeof(*uia_cache_request->prop_ids))) + return E_OUTOFMEMORY; + + uia_cache_request->prop_ids[uia_cache_request->prop_ids_count] = prop_id; + uia_cache_request->prop_ids_count++; + + uia_cache_request->cache_req.pProperties = uia_cache_request->prop_ids; + uia_cache_request->cache_req.cProperties = uia_cache_request->prop_ids_count; + + return S_OK; }
static HRESULT WINAPI uia_cache_request_AddPattern(IUIAutomationCacheRequest *iface, PATTERNID pattern_id) @@ -847,6 +876,14 @@ static HRESULT create_uia_cache_request_iface(IUIAutomationCacheRequest **out_ca uia_cache_request->cache_req.Scope = TreeScope_Element; uia_cache_request->cache_req.automationElementMode = AutomationElementMode_Full;
+ hr = IUIAutomationCacheRequest_AddProperty(&uia_cache_request->IUIAutomationCacheRequest_iface, + UIA_RuntimeIdPropertyId); + if (FAILED(hr)) + { + IUIAutomationCacheRequest_Release(&uia_cache_request->IUIAutomationCacheRequest_iface); + return hr; + } + *out_cache_req = &uia_cache_request->IUIAutomationCacheRequest_iface; return S_OK; } diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index aa297983867..d0ab909f102 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -23,7 +23,6 @@ #include "uia_private.h"
#include "wine/debug.h" -#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 2c6e41eac6d..f7e991ee4fd 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -21,6 +21,7 @@ #include "uiautomation.h" #include "uia_classes.h" #include "wine/list.h" +#include "wine/heap.h"
extern HMODULE huia_module DECLSPEC_HIDDEN;
@@ -98,6 +99,36 @@ static inline void variant_init_bool(VARIANT *v, BOOL val) V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; }
+static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) +{ + SIZE_T max_capacity, new_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(1, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = count; + + if (!*elements) + new_elements = heap_alloc_zero(new_capacity * size); + else + new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); + if (!new_elements) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + return TRUE; +} + /* uia_client.c */ HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 58d1db9bae8..a7d41248abc 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -20,7 +20,6 @@ #include "ocidl.h"
#include "wine/debug.h" -#include "wine/heap.h" #include "wine/rbtree.h" #include "initguid.h" #include "wine/iaccessible2.h"
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 138 +++++++++-- dlls/uiautomationcore/uia_com_client.c | 254 ++++++++++++++------- 2 files changed, 295 insertions(+), 97 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 41bd168e3c6..6b988504531 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10869,11 +10869,25 @@ static const struct prov_method_sequence get_cached_prop_val_seq2[] = { { 0 }, };
+static const struct prov_method_sequence get_cached_prop_val_seq3[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, + NODE_CREATE_SEQ(&Provider_child), + { &Provider, PROV_GET_PROPERTY_VALUE }, + NODE_CREATE_SEQ(&Provider_child), + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ControlTypePropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_ControlTypePropertyId */ + { 0 }, +}; + static void test_Element_cache_methods(IUIAutomation *uia_iface) { HWND hwnd = create_test_hwnd("test_Element_cache_methods class"); - IUIAutomationElement *element, *element2; + IUIAutomationElement *element, *element2, *element3; IUIAutomationCacheRequest *cache_req; + IUIAutomationElementArray *elem_arr; + int tmp_rt_id[2], i, len; IUnknown *unk_ns; HRESULT hr; VARIANT v; @@ -10938,15 +10952,14 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface)
/* RuntimeId is currently unset, so we'll get the NotSupported value. */ hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); - if (SUCCEEDED(hr)) - ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v)); VariantClear(&v);
/* Attempting to get a cached value for a non-cached property. */ hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_IsControlElementPropertyId, TRUE, &v); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); ok(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&v)); VariantClear(&v);
@@ -10961,10 +10974,9 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq");
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v)); - if (SUCCEEDED(hr)) - check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v)); + check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v)); VariantClear(&v); IUIAutomationElement_Release(element2);
@@ -10983,22 +10995,116 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) ok_method_sequence(get_cached_prop_val_seq2, "get_cached_prop_val_seq2");
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v)); - if (SUCCEEDED(hr)) - check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v)); + check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v)); VariantClear(&v);
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_IsControlElementPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v)); VariantClear(&v);
IUIAutomationElement_Release(element2); + IUIAutomationCacheRequest_Release(cache_req);
IUIAutomationElement_Release(element); ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + + /* Test cached UIAutomationType_Element properties. */ + element = create_test_element_from_hwnd(uia_iface, hwnd, TRUE); + + cache_req = NULL; + hr = IUIAutomation_CreateCacheRequest(uia_iface, &cache_req); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cache_req, "cache_req == NULL\n"); + + /* UIAutomationType_Element property. */ + hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_LabeledByPropertyId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* UIAutomationType_ElementArray property. */ + hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_ControllerForPropertyId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + element2 = NULL; + hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element2, "element2 == NULL\n"); + ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + tmp_rt_id[0] = UIA_RUNTIME_ID_PREFIX; + tmp_rt_id[1] = HandleToULong(hwnd); + hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v)); + check_runtime_id(tmp_rt_id, ARRAY_SIZE(tmp_rt_id), V_ARRAY(&v)); + VariantClear(&v); + + /* Cached IUIAutomationElement. */ + hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_LabeledByPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + + element3 = NULL; + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElement, (void **)&element3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element3, "element3 == NULL\n"); + VariantClear(&v); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element3, UIA_IsControlElementPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v)); + IUIAutomationElement_Release(element3); + VariantClear(&v); + + /* Cached IUIAutomationElementArray. */ + hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_ControllerForPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElementArray, (void **)&elem_arr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!elem_arr, "elem_arr == NULL\n"); + VariantClear(&v); + + hr = IUIAutomationElementArray_get_Length(elem_arr, &len); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(len == ARRAY_SIZE(uia_unk_arr_prop_val), "Unexpected length %d\n", len); + + for (i = 0; i < ARRAY_SIZE(uia_unk_arr_prop_val); i++) + { + hr = IUIAutomationElementArray_GetElement(elem_arr, i, &element3); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!element3, "element3 == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element3, UIA_ControlTypePropertyId, TRUE, &v); + ok(hr == S_OK, "elem[%d] Unexpected hr %#lx\n", i, hr); + ok(V_VT(&v) == VT_I4, "elem[%d] Unexpected VT %d\n", i, V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "elem[%d] Unexpected I4 %#lx\n", i, V_I4(&v)); + + IUIAutomationElement_Release(element3); + VariantClear(&v); + } + + IUIAutomationElementArray_Release(elem_arr); IUIAutomationCacheRequest_Release(cache_req); + + /* + * Reference isn't released until the element holding the cache is + * destroyed. + */ + ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + IUIAutomationElement_Release(element2); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok_method_sequence(get_cached_prop_val_seq3, "get_cached_prop_val_seq3"); + + IUIAutomationElement_Release(element); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); IUnknown_Release(unk_ns);
DestroyWindow(hwnd); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 2b499fa8bc5..6309d5a952c 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1029,6 +1029,11 @@ static HRESULT create_uia_element_array_iface(IUIAutomationElementArray **iface, return S_OK; }
+struct uia_cache_property { + int prop_id; + VARIANT prop_val; +}; + /* * IUIAutomationElement interface. */ @@ -1038,6 +1043,9 @@ struct uia_element {
BOOL from_cui8; HUIANODE node; + + struct uia_cache_property *cached_props; + int cached_props_count; };
static inline struct uia_element *impl_from_IUIAutomationElement9(IUIAutomationElement9 *iface) @@ -1079,6 +1087,15 @@ static ULONG WINAPI uia_element_Release(IUIAutomationElement9 *iface) TRACE("%p, refcount %ld\n", element, ref); if (!ref) { + if (element->cached_props_count) + { + int i; + + for (i = 0; i < element->cached_props_count; i++) + VariantClear(&element->cached_props[i].prop_val); + } + + heap_free(element->cached_props); UiaNodeRelease(element->node); heap_free(element); } @@ -1126,8 +1143,8 @@ static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface return E_NOTIMPL; }
-static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, SAFEARRAY *req_data, - BSTR tree_struct); +static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, + struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **updated_elem) { @@ -1152,7 +1169,7 @@ static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface if (FAILED(hr)) return hr;
- hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, sa, tree_struct); + hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, cache_req_struct, sa, tree_struct); if (SUCCEEDED(hr)) *updated_elem = cache_elem;
@@ -1168,45 +1185,78 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValue(IUIAutomationElement9 }
static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, HUIANODE node); -static HRESULT create_element_array_from_node_array(SAFEARRAY *sa, BOOL from_cui8, - IUIAutomationElementArray **out_elem_arr) +static HRESULT get_element_variant_from_node_variant(VARIANT *var, BOOL from_cui8, int prop_type) { - struct uia_element_array *elem_arr_data; - IUIAutomationElementArray *elem_arr; - LONG idx, lbound, elems, i; + HUIANODE node; HRESULT hr;
- *out_elem_arr = NULL; - hr = get_safearray_bounds(sa, &lbound, &elems); - if (FAILED(hr)) - return hr; - - hr = create_uia_element_array_iface(&elem_arr, elems); - if (FAILED(hr)) - return hr; + /* ReservedNotSupported interface, just return it. */ + if (V_VT(var) == VT_UNKNOWN) + return S_OK;
- elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr); - for (i = 0; i < elems; i++) + if (prop_type & UIAutomationType_Array) { - HUIANODE node; + struct uia_element_array *elem_arr_data; + IUIAutomationElementArray *elem_arr; + LONG idx, lbound, elems, i;
- idx = lbound + i; - hr = SafeArrayGetElement(sa, &idx, &node); + hr = get_safearray_bounds(V_ARRAY(var), &lbound, &elems); if (FAILED(hr)) - break; + { + VariantClear(var); + return hr; + }
- hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node); + hr = create_uia_element_array_iface(&elem_arr, elems); if (FAILED(hr)) { - UiaNodeRelease(node); - break; + VariantClear(var); + return hr; } - }
- if (SUCCEEDED(hr)) - *out_elem_arr = elem_arr; + elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr); + for (i = 0; i < elems; i++) + { + idx = lbound + i; + hr = SafeArrayGetElement(V_ARRAY(var), &idx, &node); + if (FAILED(hr)) + break; + + hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node); + if (FAILED(hr)) + { + UiaNodeRelease(node); + break; + } + } + + VariantClear(var); + if (SUCCEEDED(hr)) + { + V_VT(var) = VT_UNKNOWN; + V_UNKNOWN(var) = (IUnknown *)elem_arr; + } + else + IUIAutomationElementArray_Release(elem_arr); + } else - IUIAutomationElementArray_Release(elem_arr); + { + IUIAutomationElement *out_elem; + + hr = UiaHUiaNodeFromVariant(var, &node); + VariantClear(var); + if (FAILED(hr)) + return hr; + + hr = create_uia_element(&out_elem, from_cui8, node); + if (SUCCEEDED(hr)) + { + V_VT(var) = VT_UNKNOWN; + V_UNKNOWN(var) = (IUnknown *)out_elem; + } + else + UiaNodeRelease(node); + }
return hr; } @@ -1234,53 +1284,8 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement if (FAILED(hr)) return hr;
- switch (prop_info->type) - { - case UIAutomationType_Element: - { - IUIAutomationElement *out_elem; - HUIANODE node; - - if (V_VT(ret_val) == VT_UNKNOWN) - break; - - hr = UiaHUiaNodeFromVariant(ret_val, &node); - VariantClear(ret_val); - if (FAILED(hr)) - return hr; - - hr = create_uia_element(&out_elem, element->from_cui8, node); - if (SUCCEEDED(hr)) - { - V_VT(ret_val) = VT_UNKNOWN; - V_UNKNOWN(ret_val) = (IUnknown *)out_elem; - } - else - UiaNodeRelease(node); - - break; - } - - case UIAutomationType_ElementArray: - { - IUIAutomationElementArray *out_elem_arr; - - if (V_VT(ret_val) == VT_UNKNOWN) - break; - - hr = create_element_array_from_node_array(V_ARRAY(ret_val), element->from_cui8, &out_elem_arr); - VariantClear(ret_val); - if (SUCCEEDED(hr)) - { - V_VT(ret_val) = VT_UNKNOWN; - V_UNKNOWN(ret_val) = (IUnknown *)out_elem_arr; - } - break; - } - - default: - break; - } + if ((prop_info->type == UIAutomationType_Element) || (prop_info->type == UIAutomationType_ElementArray)) + hr = get_element_variant_from_node_variant(ret_val, element->from_cui8, prop_info->type);
return hr; } @@ -1292,11 +1297,38 @@ static HRESULT WINAPI uia_element_GetCachedPropertyValue(IUIAutomationElement9 * return E_NOTIMPL; }
+static int __cdecl uia_cached_property_id_compare(const void *a, const void *b) +{ + const PROPERTYID *prop_id = a; + const struct uia_cache_property *cache_prop = b; + + return ((*prop_id) > cache_prop->prop_id) - ((*prop_id) < cache_prop->prop_id); +} + static HRESULT WINAPI uia_element_GetCachedPropertyValueEx(IUIAutomationElement9 *iface, PROPERTYID prop_id, BOOL ignore_default, VARIANT *ret_val) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + struct uia_element *element = impl_from_IUIAutomationElement9(iface); + struct uia_cache_property *cache_prop = NULL; + + TRACE("%p, %d, %d, %p\n", iface, prop_id, ignore_default, ret_val); + + if (!ret_val) + return E_POINTER; + + VariantInit(ret_val); + if (!uia_prop_info_from_id(prop_id) || !element->cached_props) + return E_INVALIDARG; + + if (!ignore_default) + FIXME("Default values currently unimplemented\n"); + + if (!(cache_prop = bsearch(&prop_id, element->cached_props, element->cached_props_count, sizeof(*cache_prop), + uia_cached_property_id_compare))) + return E_INVALIDARG; + + VariantCopy(ret_val, &cache_prop->prop_val); + return S_OK; }
static HRESULT WINAPI uia_element_GetCurrentPatternAs(IUIAutomationElement9 *iface, PATTERNID pattern_id, @@ -2144,9 +2176,19 @@ static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, return S_OK; }
-static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, SAFEARRAY *req_data, - BSTR tree_struct) +static int __cdecl uia_compare_cache_props(const void *a, const void *b) { + struct uia_cache_property *prop1 = (struct uia_cache_property *)a; + struct uia_cache_property *prop2 = (struct uia_cache_property *)b; + + return (prop1->prop_id > prop2->prop_id) - (prop1->prop_id < prop2->prop_id); +} + +static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, + struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct) +{ + IUIAutomationElement *element = NULL; + struct uia_element *elem_data; HUIANODE node; LONG idx[2]; HRESULT hr; @@ -2165,11 +2207,61 @@ static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, B goto exit; VariantClear(&v);
- hr = create_uia_element(iface, from_cui8, node); + hr = create_uia_element(&element, from_cui8, node); + if (FAILED(hr)) + goto exit; + + elem_data = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)element); + if (cache_req->cProperties) + { + LONG i; + + elem_data->cached_props = heap_alloc_zero(sizeof(*elem_data->cached_props) * cache_req->cProperties); + if (!elem_data->cached_props) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + elem_data->cached_props_count = cache_req->cProperties; + for (i = 0; i < cache_req->cProperties; i++) + { + const struct uia_prop_info *prop_info = uia_prop_info_from_id(cache_req->pProperties[i]); + + elem_data->cached_props[i].prop_id = prop_info->prop_id; + + idx[0] = 0; + idx[1] = 1 + i; + hr = SafeArrayGetElement(req_data, idx, &elem_data->cached_props[i].prop_val); + if (FAILED(hr)) + goto exit; + + if ((prop_info->type == UIAutomationType_Element) || (prop_info->type == UIAutomationType_ElementArray)) + { + hr = get_element_variant_from_node_variant(&elem_data->cached_props[i].prop_val, from_cui8, + prop_info->type); + if (FAILED(hr)) + goto exit; + } + } + + /* + * Sort the array of cached properties by property ID so that we can + * access the values with bsearch. + */ + qsort(elem_data->cached_props, elem_data->cached_props_count, sizeof(*elem_data->cached_props), + uia_compare_cache_props); + } + + *iface = element;
exit: if (FAILED(hr)) + { WARN("Failed to create element from cache request, hr %#lx\n", hr); + if (element) + IUIAutomationElement_Release(element); + }
SysFreeString(tree_struct); return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 667 ++++++++++++++++++++- 1 file changed, 644 insertions(+), 23 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6b988504531..82458bba6c6 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -122,6 +122,12 @@ static BOOL check_variant_bool(VARIANT *v, BOOL val) return FALSE; }
+static void variant_init_bool(VARIANT *v, BOOL val) +{ + V_VT(v) = VT_BOOL; + V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; +} + static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) { IUnknown *unk1, *unk2; @@ -1650,6 +1656,9 @@ HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface, if (This->prop_override[i].prop_id == prop_id) { *ret_val = This->prop_override[i].val; + if (V_VT(ret_val) == VT_UNKNOWN) + IUnknown_AddRef(V_UNKNOWN(ret_val)); + return S_OK; } } @@ -9093,6 +9102,13 @@ static const struct prov_method_sequence find_seq1[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child2_child_child, FRAG_GET_RUNTIME_ID }, { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9116,6 +9132,9 @@ static const struct prov_method_sequence find_seq2[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9135,6 +9154,8 @@ static const struct prov_method_sequence find_seq3[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 }, @@ -9144,6 +9165,7 @@ static const struct prov_method_sequence find_seq4[] = { { &Provider, FRAG_GET_RUNTIME_ID }, { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 }, }; @@ -9157,12 +9179,15 @@ static const struct prov_method_sequence find_seq5[] = { NODE_CREATE_SEQ(&Provider), { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ NODE_CREATE_SEQ(&Provider), + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 }, };
static const struct prov_method_sequence find_seq6[] = { + { &Provider_child, FRAG_GET_RUNTIME_ID }, { &Provider_child, FRAG_GET_RUNTIME_ID }, { &Provider_child, FRAG_GET_RUNTIME_ID }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9198,6 +9223,10 @@ static const struct prov_method_sequence find_seq7[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9235,6 +9264,10 @@ static const struct prov_method_sequence find_seq8[] = { { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ NODE_CREATE_SEQ(&Provider), { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9274,6 +9307,9 @@ static const struct prov_method_sequence find_seq9[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2_child, FRAG_GET_RUNTIME_ID }, { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { &Provider_child2_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ @@ -9321,6 +9357,7 @@ static const struct prov_method_sequence find_seq10[] = { { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ /* Only done on Win10v1507 and below. */ { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child2, FRAG_GET_RUNTIME_ID }, { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 }, }; @@ -9339,6 +9376,7 @@ static const struct prov_method_sequence find_seq11[] = { NODE_CREATE_SEQ(&Provider_child_child2), { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 }, }; @@ -9353,8 +9391,8 @@ static void test_UiaFind(void) struct UiaCacheRequest cache_req; struct UiaFindParams find_params; const WCHAR *exp_tree_struct[7]; + int exp_offset[7], cache_prop; HUIANODE node, node2; - int exp_offset[7]; HRESULT hr; VARIANT v;
@@ -9392,7 +9430,8 @@ static void test_UiaFind(void) * maximum depth of -1 will search the entire tree. */ out_req = offsets = tree_structs = NULL; - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + cache_prop = UIA_RuntimeIdPropertyId; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, -1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9413,7 +9452,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[6], L"Main", L"Provider_child2_child_child", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 7; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9435,7 +9474,7 @@ static void test_UiaFind(void) /* * Maximum find depth of 1, find first is FALSE, exclude root is FALSE. */ - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9448,7 +9487,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 3; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9471,7 +9510,7 @@ static void test_UiaFind(void) /* * Maximum find depth of 1, find first is FALSE, exclude root is TRUE. */ - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9483,7 +9522,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 2; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9506,7 +9545,7 @@ static void test_UiaFind(void) * Maximum find depth of 1, find first is TRUE, exclude root is TRUE. Will * retrieve only Provider_child. */ - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, TRUE, TRUE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9515,7 +9554,8 @@ static void test_UiaFind(void)
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; + exp_elems[0] = 1; + exp_elems[1] = 2;
idx[0] = idx[1] = 0; hr = SafeArrayGetElement(out_req, idx, &v); @@ -9549,7 +9589,7 @@ static void test_UiaFind(void) * way to check if it has navigated back to the node that began the * search, so it will get siblings. */ - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9560,7 +9600,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 2; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9586,7 +9626,7 @@ static void test_UiaFind(void) * search, so it will stop at Provider_child. */ Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; - set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9594,7 +9634,8 @@ static void test_UiaFind(void)
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; + exp_elems[0] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9638,7 +9679,7 @@ static void test_UiaFind(void) set_provider_prop_override(&Provider_child_child, &prop_override, 1); set_provider_prop_override(&Provider_child_child2, &prop_override, 1);
- set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9653,7 +9694,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 4; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9691,7 +9732,7 @@ static void test_UiaFind(void) set_provider_prop_override(&Provider_child_child, &prop_override, 1); set_provider_prop_override(&Provider_child_child2, &prop_override, 1);
- set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9706,7 +9747,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 4; - exp_elems[1] = 1; + exp_elems[1] = 2;
idx[0] = 2; idx[1] = 0; @@ -9758,7 +9799,7 @@ static void test_UiaFind(void) set_provider_prop_override(&Provider_child2, &prop_override, 1); set_provider_prop_override(&Provider_child2_child, &prop_override, 1);
- set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9771,7 +9812,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2_child", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 3; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9813,7 +9854,7 @@ static void test_UiaFind(void) set_provider_prop_override(&Provider_child, &prop_override, 1); set_provider_prop_override(&Provider_child2, &prop_override, 1);
- set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9823,7 +9864,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 1; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -9860,7 +9901,7 @@ static void test_UiaFind(void)
set_provider_prop_override(&Provider_child_child2, &prop_override, 1);
- set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, &cache_prop, 1, NULL, 0, AutomationElementMode_Full); set_find_params(&find_params, -1, TRUE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); @@ -9870,7 +9911,7 @@ static void test_UiaFind(void) add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 1; - exp_elems[1] = 1; + exp_elems[1] = 2;
test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
@@ -11111,6 +11152,585 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) UnregisterClassA("test_Element_cache_methods class", NULL); }
+static const struct prov_method_sequence element_find_start_seq[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_LabeledByPropertyId */ + NODE_CREATE_SEQ(&Provider), + { 0 } +}; + +/* + * Identical to find_seq7, except default cache request used by FindAll + * doesn't cache UIA_RuntimeIdPropertyId. + */ +static const struct prov_method_sequence element_find_seq1[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +/* + * Identical to find_seq11, except default cache request used by FindFirst + * doesn't cache UIA_RuntimeIdPropertyId. + */ +static const struct prov_method_sequence element_find_seq2[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +struct exp_elem_desc { + struct Provider *elem_prov; + struct node_provider_desc prov_desc; + ULONG exp_refcnt; + ULONG exp_release_refcnt; +}; + +static void set_elem_desc(struct exp_elem_desc *desc, struct Provider *prov, HWND hwnd, DWORD pid, ULONG exp_refcnt, + ULONG exp_release_refcnt) +{ + desc->elem_prov = prov; + init_node_provider_desc(&desc->prov_desc, pid, hwnd); + desc->exp_refcnt = exp_refcnt; + desc->exp_release_refcnt = exp_release_refcnt; +} + +#define test_uia_element_arr( elem_arr, exp_elems, exp_elems_count ) \ + test_uia_element_arr_( (elem_arr), (exp_elems), (exp_elems_count), __FILE__, __LINE__) +static void test_uia_element_arr_(IUIAutomationElementArray *elem_arr, struct exp_elem_desc *exp_elems, int exp_elems_count, + const char *file, int line) +{ + int i, arr_length; + HRESULT hr; + + hr = IUIAutomationElementArray_get_Length(elem_arr, &arr_length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(hr == S_OK, "IUIAutomationElementArray_get_Length: Unexpected hr %#lx\n", hr); + ok_(file, line)(arr_length == exp_elems_count, "Unexpected arr_length %d.\n", arr_length); + + for (i = 0; i < arr_length; i++) + { + struct exp_elem_desc *desc = &exp_elems[i]; + IUIAutomationElement *element; + VARIANT v; + + ok_(file, line)(desc->elem_prov->ref == desc->exp_refcnt, "elem[%d]: Unexpected refcnt %ld\n", i, exp_elems[i].elem_prov->ref); + + VariantInit(&v); + element = NULL; + hr = IUIAutomationElementArray_GetElement(elem_arr, i, &element); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element, "element == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + test_node_provider_desc_(&exp_elems[i].prov_desc, V_BSTR(&v), file, line); + VariantClear(&v); + IUIAutomationElement_Release(element); + } + + IUIAutomationElementArray_Release(elem_arr); + + for (i = 0; i < arr_length; i++) + { + struct exp_elem_desc *desc = &exp_elems[i]; + + ok_(file, line)(desc->elem_prov->ref == desc->exp_release_refcnt, "elem[%d]: Unexpected refcnt %ld\n", i, exp_elems[i].elem_prov->ref); + } +} + +static void test_Element_Find(IUIAutomation *uia_iface) +{ + HWND hwnd = create_test_hwnd("test_Element_Find class"); + IUIAutomationCondition *condition, *condition2; + struct Provider_prop_override prop_override; + struct exp_elem_desc exp_elems[7] = { 0 }; + IUIAutomationElement *element, *element2; + IUIAutomationElementArray *element_arr; + IUIAutomationCacheRequest *cache_req; + HRESULT hr; + VARIANT v; + + element = create_test_element_from_hwnd(uia_iface, hwnd, TRUE); + + /* + * The COM API has no equivalent to UiaNodeFromProvider, so the only way + * we can get an initial element is with ElementFromHandle. This means our + * element representing Provider will have an HWND associated, which + * doesn't match our old UiaFind tests. To work around this, make Provider + * return itself for the UIA_LabeledByPropertyId to get an element without + * an HWND associated. + */ + VariantInit(&v); + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = (IUnknown *)&Provider.IRawElementProviderSimple_iface; + set_property_override(&prop_override, UIA_LabeledByPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + Provider.hwnd = NULL; + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_LabeledByPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElement, (void **)&element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element2, "element2 == NULL\n"); + VariantClear(&v); + ok_method_sequence(element_find_start_seq, "element_find_start_seq"); + set_provider_prop_override(&Provider, NULL, 0); + + IUIAutomationElement_Release(element); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + element = element2; + element2 = NULL; + + initialize_provider_tree(TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + provider_add_child(&Provider, &Provider_child); + provider_add_child(&Provider, &Provider_child2); + provider_add_child(&Provider_child, &Provider_child_child); + provider_add_child(&Provider_child, &Provider_child_child2); + provider_add_child(&Provider_child2, &Provider_child2_child); + provider_add_child(&Provider_child2_child, &Provider_child2_child_child); + + /* + * In order to match the tests from test_UiaFind(), we need to create a + * custom IUIAutomationCacheRequest with a ConditionType_True treeview. + * The default cache request also caches the RuntimeId property. + */ + cache_req = NULL; + hr = IUIAutomation_CreateCacheRequest(uia_iface, &cache_req); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cache_req, "cache_req == NULL\n"); + + /* Set view condition to ConditionType_True. */ + condition = NULL; + hr = IUIAutomation_CreateTrueCondition(uia_iface, &condition); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!condition, "condition == NULL\n"); + + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, condition); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* + * Equivalent to: Maximum find depth of -1, find first is FALSE, exclude + * root is FALSE. + */ + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_SubTree, condition, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[4], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[4].prov_desc, L"Main", L"Provider_child2", TRUE); + set_elem_desc(&exp_elems[5], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[5].prov_desc, L"Main", L"Provider_child2_child", TRUE); + set_elem_desc(&exp_elems[6], &Provider_child2_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[6].prov_desc, L"Main", L"Provider_child2_child_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 7); + ok_method_sequence(find_seq1, "find_seq1"); + } + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude root + * is FALSE. + */ + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 3); + ok_method_sequence(find_seq2, "find_seq2"); + } + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude root + * is TRUE. + */ + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Children, condition, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 2); + ok_method_sequence(find_seq3, "find_seq3"); + } + + /* + * Equivalent to: Maximum find depth of 1, find first is TRUE, exclude + * root is TRUE. element2 now represents Provider_child. + */ + hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_Children, condition, cache_req, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + } + + ok_method_sequence(find_seq4, "find_seq4"); + } + + if (element2) + { + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child doesn't have a runtime id for UI + * Automation to use as a way to check if it has navigated back to the + * node that began the search, so it will get siblings. + */ + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 2); + ok_method_sequence(find_seq5, "find_seq5"); + } + + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child now has a runtime ID, so we don't get + * its sibling. + */ + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + IUIAutomationElement_Release(element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 1); + ok_method_sequence(find_seq6, "find_seq6"); + } + initialize_provider_tree(FALSE); + } + + IUIAutomationCondition_Release(condition); + + /* condition is now UIA_IsContentElementPropertyId == FALSE. */ + condition = NULL; + variant_init_bool(&v, FALSE); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_IsContentElementPropertyId, v, &condition); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!condition, "condition == NULL\n"); + + /* Set view condition to this property condition. */ + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, condition); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* condition2 is UIA_IsControlElementPropertyId == TRUE. */ + condition2 = NULL; + variant_init_bool(&v, TRUE); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_IsControlElementPropertyId, v, &condition2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!condition2, "condition2 == NULL\n"); + + /* Override UIA_IsContentElementPropertyId, set it to FALSE. */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude + * root is FALSE. The cache request view condition is used to determine + * tree depth, if an element matches the cache request view condition, + * depth is incremented. Since Provider_child does not, Provider_child_child, + * Provider_child_child2, and Provider_child2 are all considered to be at + * depth 1. + */ + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(find_seq7, "find_seq7"); + } + initialize_provider_tree(FALSE); + + /* + * Same test as before, except now Provider has a runtime ID. + */ + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + /* element2 now represents Provider_child_child2. */ + hr = IUIAutomationElementArray_GetElement(element_arr, 2, &element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(find_seq8, "find_seq8"); + } + initialize_provider_tree(FALSE); + + if (element2) + { + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2_child, &prop_override, 1); + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, + * exclude root is FALSE. Starting at Provider_child_child2, find + * will be able to traverse the tree in the same order as it would + * if we had started at the tree root Provider, retrieving + * Provider_child2 as a sibling and Provider_child2_child as a node + * at depth 1. + */ + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUIAutomationElement_Release(element2); + + set_elem_desc(&exp_elems[0], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2_child", TRUE); + test_uia_element_arr(element_arr, exp_elems, 3); + ok_method_sequence(find_seq9, "find_seq9"); + } + initialize_provider_tree(FALSE); + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude + * root is TRUE. Exclude root applies to the first node that matches the + * view condition, and not the node that is passed into UiaFind(). Since + * Provider doesn't match our view condition here, Provider_child will be + * excluded. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider_child, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Children, condition2, cache_req, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 1); + ok_method_sequence(find_seq10, "find_seq10"); + } + initialize_provider_tree(FALSE); + + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + /* + * Equivalent to: Maximum find depth of -1, find first is TRUE, exclude + * root is FALSE. Provider_child_child2 is the only element in the tree + * to match our condition. + */ + hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_SubTree, condition, cache_req, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); + } + + IUIAutomationElement_Release(element2); + ok_method_sequence(find_seq11, "find_seq11"); + } + initialize_provider_tree(FALSE); + + IUIAutomationCondition_Release(condition); + IUIAutomationCondition_Release(condition2); + IUIAutomationCacheRequest_Release(cache_req); + + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, + * exclude root is FALSE. FindAll() uses the default + * IUIAutomationCacheRequest, which uses the ControlView condition + * as its view condition. Since Provider_child doesn't match this view + * condition, it isn't a child of Provider in this treeview. No property + * values are cached by the default internal cache request, so we need to + * use a separate method sequence. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider_child, &prop_override, 1); + + /* condition is now UIA_IsContentElementPropertyId == TRUE. */ + variant_init_bool(&v, TRUE); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_IsContentElementPropertyId, v, &condition); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!condition, "cond == NULL\n"); + + hr = IUIAutomationElement_FindAll(element, TreeScope_Element | TreeScope_Children, condition, &element_arr); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(element_find_seq1, "element_find_seq1"); + } + initialize_provider_tree(FALSE); + + /* + * Equivalent to: Maximum find depth of -1, find first is TRUE, + * exclude root is FALSE. FindFirst() also uses the default cache request. + * Provider_child_child2 will be the first provider in the tree to match + * this view condition, so it will be returned. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + set_provider_prop_override(&Provider_child, &prop_override, 1); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + + hr = IUIAutomationElement_FindFirst(element, TreeScope_SubTree, condition, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); + } + + IUIAutomationElement_Release(element2); + ok_method_sequence(element_find_seq2, "element_find_seq2"); + } + + initialize_provider_tree(TRUE); + + IUIAutomationCondition_Release(condition); + IUIAutomationElement_Release(element); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + + DestroyWindow(hwnd); + UnregisterClassA("test_Element_Find class", NULL); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -11215,6 +11835,7 @@ static void test_CUIAutomation(void) test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface); test_Element_cache_methods(uia_iface); + test_Element_Find(uia_iface);
IUIAutomation_Release(uia_iface); CoUninitialize();
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 232 ++++++++++----------- dlls/uiautomationcore/uia_com_client.c | 146 +++++++++++-- 2 files changed, 235 insertions(+), 143 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 82458bba6c6..5f6b4fd10aa 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -11361,63 +11361,57 @@ static void test_Element_Find(IUIAutomation *uia_iface) * root is FALSE. */ hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_SubTree, condition, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child", TRUE); - set_elem_desc(&exp_elems[3], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child_child2", TRUE); - set_elem_desc(&exp_elems[4], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[4].prov_desc, L"Main", L"Provider_child2", TRUE); - set_elem_desc(&exp_elems[5], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[5].prov_desc, L"Main", L"Provider_child2_child", TRUE); - set_elem_desc(&exp_elems[6], &Provider_child2_child_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[6].prov_desc, L"Main", L"Provider_child2_child_child", TRUE); - - test_uia_element_arr(element_arr, exp_elems, 7); - ok_method_sequence(find_seq1, "find_seq1"); - } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[4], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[4].prov_desc, L"Main", L"Provider_child2", TRUE); + set_elem_desc(&exp_elems[5], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[5].prov_desc, L"Main", L"Provider_child2_child", TRUE); + set_elem_desc(&exp_elems[6], &Provider_child2_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[6].prov_desc, L"Main", L"Provider_child2_child_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 7); + ok_method_sequence(find_seq1, "find_seq1");
/* * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude root * is FALSE. */ hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2", TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 3); - ok_method_sequence(find_seq2, "find_seq2"); - } + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 3); + ok_method_sequence(find_seq2, "find_seq2");
/* * Equivalent to: Maximum find depth of 1, find first is FALSE, exclude root * is TRUE. */ hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Children, condition, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 2); - ok_method_sequence(find_seq3, "find_seq3"); - } + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 2); + ok_method_sequence(find_seq3, "find_seq3");
/* * Equivalent to: Maximum find depth of 1, find first is TRUE, exclude @@ -11518,21 +11512,19 @@ static void test_Element_Find(IUIAutomation *uia_iface) * depth 1. */ hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); - set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 4); - ok_method_sequence(find_seq7, "find_seq7"); - } + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(find_seq7, "find_seq7"); initialize_provider_tree(FALSE);
/* @@ -11547,56 +11539,52 @@ static void test_Element_Find(IUIAutomation *uia_iface) set_provider_prop_override(&Provider_child_child2, &prop_override, 1);
hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); - set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); - - /* element2 now represents Provider_child_child2. */ - hr = IUIAutomationElementArray_GetElement(element_arr, 2, &element2); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 4); - ok_method_sequence(find_seq8, "find_seq8"); - } + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + /* element2 now represents Provider_child_child2. */ + hr = IUIAutomationElementArray_GetElement(element_arr, 2, &element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(find_seq8, "find_seq8"); initialize_provider_tree(FALSE);
- if (element2) - { - variant_init_bool(&v, FALSE); - set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); - set_provider_prop_override(&Provider_child_child2, &prop_override, 1); - set_provider_prop_override(&Provider_child2, &prop_override, 1); - set_provider_prop_override(&Provider_child2_child, &prop_override, 1); + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2_child, &prop_override, 1);
- /* - * Equivalent to: Maximum find depth of 1, find first is FALSE, - * exclude root is FALSE. Starting at Provider_child_child2, find - * will be able to traverse the tree in the same order as it would - * if we had started at the tree root Provider, retrieving - * Provider_child2 as a sibling and Provider_child2_child as a node - * at depth 1. - */ - hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IUIAutomationElement_Release(element2); + /* + * Equivalent to: Maximum find depth of 1, find first is FALSE, + * exclude root is FALSE. Starting at Provider_child_child2, find + * will be able to traverse the tree in the same order as it would + * if we had started at the tree root Provider, retrieving + * Provider_child2 as a sibling and Provider_child2_child as a node + * at depth 1. + */ + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element | TreeScope_Children, condition2, cache_req, &element_arr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUIAutomationElement_Release(element2);
- set_elem_desc(&exp_elems[0], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child_child2", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2_child", TRUE); - test_uia_element_arr(element_arr, exp_elems, 3); - ok_method_sequence(find_seq9, "find_seq9"); - } + set_elem_desc(&exp_elems[0], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child2_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child2_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 3); + ok_method_sequence(find_seq9, "find_seq9"); initialize_provider_tree(FALSE);
/* @@ -11611,15 +11599,13 @@ static void test_Element_Find(IUIAutomation *uia_iface) set_provider_prop_override(&Provider_child, &prop_override, 1); set_provider_prop_override(&Provider_child2, &prop_override, 1); hr = IUIAutomationElement_FindAllBuildCache(element, TreeScope_Children, condition2, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child2", TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 1); - ok_method_sequence(find_seq10, "find_seq10"); - } + set_elem_desc(&exp_elems[0], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 1); + ok_method_sequence(find_seq10, "find_seq10"); initialize_provider_tree(FALSE);
variant_init_bool(&v, FALSE); @@ -11674,21 +11660,19 @@ static void test_Element_Find(IUIAutomation *uia_iface) ok(!!condition, "cond == NULL\n");
hr = IUIAutomationElement_FindAll(element, TreeScope_Element | TreeScope_Children, condition, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); - set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); - set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- test_uia_element_arr(element_arr, exp_elems, 4); - ok_method_sequence(element_find_seq1, "element_find_seq1"); - } + set_elem_desc(&exp_elems[0], &Provider, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child_child", TRUE); + set_elem_desc(&exp_elems[2], &Provider_child_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[2].prov_desc, L"Main", L"Provider_child_child2", TRUE); + set_elem_desc(&exp_elems[3], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[3].prov_desc, L"Main", L"Provider_child2", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 4); + ok_method_sequence(element_find_seq1, "element_find_seq1"); initialize_provider_tree(FALSE);
/* diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 6309d5a952c..850bfb499e1 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -876,14 +876,6 @@ static HRESULT create_uia_cache_request_iface(IUIAutomationCacheRequest **out_ca uia_cache_request->cache_req.Scope = TreeScope_Element; uia_cache_request->cache_req.automationElementMode = AutomationElementMode_Full;
- hr = IUIAutomationCacheRequest_AddProperty(&uia_cache_request->IUIAutomationCacheRequest_iface, - UIA_RuntimeIdPropertyId); - if (FAILED(hr)) - { - IUIAutomationCacheRequest_Release(&uia_cache_request->IUIAutomationCacheRequest_iface); - return hr; - } - *out_cache_req = &uia_cache_request->IUIAutomationCacheRequest_iface; return S_OK; } @@ -1125,10 +1117,27 @@ static HRESULT WINAPI uia_element_FindFirst(IUIAutomationElement9 *iface, enum T static HRESULT WINAPI uia_element_FindAll(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationElementArray **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + IUIAutomationCacheRequest *cache_req; + HRESULT hr; + + TRACE("%p, %#x, %p, %p\n", iface, scope, condition, found); + + if (!found) + return E_POINTER; + + *found = NULL; + hr = create_uia_cache_request_iface(&cache_req); + if (FAILED(hr)) + return hr; + + hr = IUIAutomationElement9_FindAllBuildCache(iface, scope, condition, cache_req, found); + IUIAutomationCacheRequest_Release(cache_req); + + return hr; }
+static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, + struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_FindFirstBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **found) { @@ -1139,12 +1148,97 @@ static HRESULT WINAPI uia_element_FindFirstBuildCache(IUIAutomationElement9 *ifa static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationCacheRequest *cache_req, IUIAutomationElementArray **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + struct uia_element *element = impl_from_IUIAutomationElement9(iface); + LONG lbound_offsets, lbound_tree_structs, elems_count; + struct uia_element_array *element_array_data; + struct UiaFindParams find_params = { 0 }; + struct UiaCacheRequest *cache_req_struct; + SAFEARRAY *sa, *tree_structs, *offsets; + IUIAutomationElementArray *array; + HRESULT hr; + int i; + + TRACE("%p, %#x, %p, %p, %p\n", iface, scope, condition, cache_req, found); + + if (!found) + return E_POINTER; + + *found = array = NULL; + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + return hr; + + hr = get_uia_condition_struct_from_iface(condition, &find_params.pFindCondition); + if (FAILED(hr)) + return hr; + + if (!scope || (scope & (~TreeScope_SubTree))) + return E_INVALIDARG; + + if (scope & TreeScope_Element) + find_params.ExcludeRoot = FALSE; + else + find_params.ExcludeRoot = TRUE; + + if (scope & TreeScope_Descendants) + find_params.MaxDepth = -1; + else if (scope & TreeScope_Children) + find_params.MaxDepth = 1; + else + find_params.MaxDepth = 0; + + sa = offsets = tree_structs = NULL; + hr = UiaFind(element->node, &find_params, cache_req_struct, &sa, &offsets, &tree_structs); + if (FAILED(hr) || !sa) + goto exit; + + hr = get_safearray_bounds(tree_structs, &lbound_tree_structs, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = get_safearray_bounds(offsets, &lbound_offsets, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = create_uia_element_array_iface(&array, elems_count); + if (FAILED(hr)) + goto exit; + + element_array_data = impl_from_IUIAutomationElementArray(array); + for (i = 0; i < elems_count; i++) + { + BSTR tree_struct_str; + LONG offset_idx, idx; + + idx = lbound_offsets + i; + hr = SafeArrayGetElement(offsets, &idx, &offset_idx); + if (FAILED(hr)) + goto exit; + + idx = lbound_tree_structs + i; + hr = SafeArrayGetElement(tree_structs, &idx, &tree_struct_str); + if (FAILED(hr)) + goto exit; + + hr = create_uia_element_from_cache_req(&element_array_data->elements[i], element->from_cui8, + cache_req_struct, offset_idx, sa, tree_struct_str); + if (FAILED(hr)) + goto exit; + } + + *found = array; + +exit: + if (FAILED(hr) && array) + IUIAutomationElementArray_Release(array); + + SafeArrayDestroy(tree_structs); + SafeArrayDestroy(offsets); + SafeArrayDestroy(sa); + + return hr; }
-static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, - struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **updated_elem) { @@ -1169,7 +1263,7 @@ static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface if (FAILED(hr)) return hr;
- hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, cache_req_struct, sa, tree_struct); + hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, cache_req_struct, 0, sa, tree_struct); if (SUCCEEDED(hr)) *updated_elem = cache_elem;
@@ -2185,7 +2279,7 @@ static int __cdecl uia_compare_cache_props(const void *a, const void *b) }
static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, - struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct) + struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct) { IUIAutomationElement *element = NULL; struct uia_element *elem_data; @@ -2197,7 +2291,8 @@ static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, B *iface = NULL;
VariantInit(&v); - idx[0] = idx[1] = 0; + idx[0] = start_idx; + idx[1] = 0; hr = SafeArrayGetElement(req_data, idx, &v); if (FAILED(hr)) goto exit; @@ -2230,7 +2325,7 @@ static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, B
elem_data->cached_props[i].prop_id = prop_info->prop_id;
- idx[0] = 0; + idx[0] = start_idx; idx[1] = 1 + i; hr = SafeArrayGetElement(req_data, idx, &elem_data->cached_props[i].prop_val); if (FAILED(hr)) @@ -2445,9 +2540,22 @@ static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface,
static HRESULT WINAPI uia_iface_CreateCacheRequest(IUIAutomation6 *iface, IUIAutomationCacheRequest **out_cache_req) { + HRESULT hr; + TRACE("%p, %p\n", iface, out_cache_req);
- return create_uia_cache_request_iface(out_cache_req); + hr = create_uia_cache_request_iface(out_cache_req); + if (FAILED(hr)) + return hr; + + hr = IUIAutomationCacheRequest_AddProperty(*out_cache_req, UIA_RuntimeIdPropertyId); + if (FAILED(hr)) + { + IUIAutomationCacheRequest_Release(*out_cache_req); + *out_cache_req = NULL; + } + + return hr; }
static HRESULT WINAPI uia_iface_CreateTrueCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 136 +++++++++------------ dlls/uiautomationcore/uia_com_client.c | 120 +++++++++++++++--- 2 files changed, 161 insertions(+), 95 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 5f6b4fd10aa..2a4f87c7b59 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -11418,62 +11418,53 @@ static void test_Element_Find(IUIAutomation *uia_iface) * root is TRUE. element2 now represents Provider_child. */ hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_Children, condition, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } - - ok_method_sequence(find_seq4, "find_seq4"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); }
- if (element2) - { - /* - * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude - * root is FALSE. Provider_child doesn't have a runtime id for UI - * Automation to use as a way to check if it has navigated back to the - * node that began the search, so it will get siblings. - */ - hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + ok_method_sequence(find_seq4, "find_seq4");
- test_uia_element_arr(element_arr, exp_elems, 2); - ok_method_sequence(find_seq5, "find_seq5"); - } + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child doesn't have a runtime id for UI + * Automation to use as a way to check if it has navigated back to the + * node that began the search, so it will get siblings. + */ + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- /* - * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude - * root is FALSE. Provider_child now has a runtime ID, so we don't get - * its sibling. - */ - Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; - hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); - IUIAutomationElement_Release(element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE);
- test_uia_element_arr(element_arr, exp_elems, 1); - ok_method_sequence(find_seq6, "find_seq6"); - } - initialize_provider_tree(FALSE); - } + test_uia_element_arr(element_arr, exp_elems, 2); + ok_method_sequence(find_seq5, "find_seq5"); + + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child now has a runtime ID, so we don't get + * its sibling. + */ + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + IUIAutomationElement_Release(element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 1); + ok_method_sequence(find_seq6, "find_seq6"); + initialize_provider_tree(FALSE);
IUIAutomationCondition_Release(condition);
@@ -11618,22 +11609,20 @@ static void test_Element_Find(IUIAutomation *uia_iface) * to match our condition. */ hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_SubTree, condition, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); - VariantClear(&v); - } - - IUIAutomationElement_Release(element2); - ok_method_sequence(find_seq11, "find_seq11"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); } + + IUIAutomationElement_Release(element2); + ok_method_sequence(find_seq11, "find_seq11"); initialize_provider_tree(FALSE);
IUIAutomationCondition_Release(condition); @@ -11688,23 +11677,20 @@ static void test_Element_Find(IUIAutomation *uia_iface) set_provider_prop_override(&Provider_child_child, &prop_override, 1);
hr = IUIAutomationElement_FindFirst(element, TreeScope_SubTree, condition, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); - VariantClear(&v); - } - - IUIAutomationElement_Release(element2); - ok_method_sequence(element_find_seq2, "element_find_seq2"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); }
+ IUIAutomationElement_Release(element2); + ok_method_sequence(element_find_seq2, "element_find_seq2"); initialize_provider_tree(TRUE);
IUIAutomationCondition_Release(condition); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 850bfb499e1..4b2e5cba741 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1110,8 +1110,23 @@ static HRESULT WINAPI uia_element_GetRuntimeId(IUIAutomationElement9 *iface, SAF static HRESULT WINAPI uia_element_FindFirst(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationElement **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + IUIAutomationCacheRequest *cache_req; + HRESULT hr; + + TRACE("%p, %#x, %p, %p\n", iface, scope, condition, found); + + if (!found) + return E_POINTER; + + *found = NULL; + hr = create_uia_cache_request_iface(&cache_req); + if (FAILED(hr)) + return hr; + + hr = IUIAutomationElement9_FindFirstBuildCache(iface, scope, condition, cache_req, found); + IUIAutomationCacheRequest_Release(cache_req); + + return hr; }
static HRESULT WINAPI uia_element_FindAll(IUIAutomationElement9 *iface, enum TreeScope scope, @@ -1136,13 +1151,93 @@ static HRESULT WINAPI uia_element_FindAll(IUIAutomationElement9 *iface, enum Tre return hr; }
+static HRESULT set_find_params_struct(struct UiaFindParams *params, IUIAutomationCondition *cond, int scope, + BOOL find_first) +{ + HRESULT hr; + + hr = get_uia_condition_struct_from_iface(cond, ¶ms->pFindCondition); + if (FAILED(hr)) + return hr; + + if (!scope || (scope & (~TreeScope_SubTree))) + return E_INVALIDARG; + + params->FindFirst = find_first; + if (scope & TreeScope_Element) + params->ExcludeRoot = FALSE; + else + params->ExcludeRoot = TRUE; + + if (scope & TreeScope_Descendants) + params->MaxDepth = -1; + else if (scope & TreeScope_Children) + params->MaxDepth = 1; + else + params->MaxDepth = 0; + + return S_OK; +} + static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_FindFirstBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + struct uia_element *element = impl_from_IUIAutomationElement9(iface); + LONG lbound_offsets, lbound_tree_structs, elems_count, offset_idx; + struct UiaFindParams find_params = { 0 }; + struct UiaCacheRequest *cache_req_struct; + SAFEARRAY *sa, *tree_structs, *offsets; + IUIAutomationElement *elem; + BSTR tree_struct_str; + HRESULT hr; + + TRACE("%p, %#x, %p, %p, %p\n", iface, scope, condition, cache_req, found); + + if (!found) + return E_POINTER; + + *found = elem = NULL; + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + return hr; + + hr = set_find_params_struct(&find_params, condition, scope, TRUE); + if (FAILED(hr)) + return hr; + + sa = offsets = tree_structs = NULL; + hr = UiaFind(element->node, &find_params, cache_req_struct, &sa, &offsets, &tree_structs); + if (FAILED(hr) || !sa) + goto exit; + + hr = get_safearray_bounds(tree_structs, &lbound_tree_structs, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayGetElement(tree_structs, &lbound_tree_structs, &tree_struct_str); + if (FAILED(hr)) + goto exit; + + hr = get_safearray_bounds(offsets, &lbound_offsets, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayGetElement(offsets, &lbound_offsets, &offset_idx); + if (FAILED(hr)) + goto exit; + + hr = create_uia_element_from_cache_req(&elem, element->from_cui8, cache_req_struct, offset_idx, sa, tree_struct_str); + if (SUCCEEDED(hr)) + *found = elem; + +exit: + SafeArrayDestroy(tree_structs); + SafeArrayDestroy(offsets); + SafeArrayDestroy(sa); + + return hr; }
static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, @@ -1168,25 +1263,10 @@ static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface if (FAILED(hr)) return hr;
- hr = get_uia_condition_struct_from_iface(condition, &find_params.pFindCondition); + hr = set_find_params_struct(&find_params, condition, scope, FALSE); if (FAILED(hr)) return hr;
- if (!scope || (scope & (~TreeScope_SubTree))) - return E_INVALIDARG; - - if (scope & TreeScope_Element) - find_params.ExcludeRoot = FALSE; - else - find_params.ExcludeRoot = TRUE; - - if (scope & TreeScope_Descendants) - find_params.MaxDepth = -1; - else if (scope & TreeScope_Children) - find_params.MaxDepth = 1; - else - find_params.MaxDepth = 0; - sa = offsets = tree_structs = NULL; hr = UiaFind(element->node, &find_params, cache_req_struct, &sa, &offsets, &tree_structs); if (FAILED(hr) || !sa)
This merge request was approved by Esme Povirk.