Implement UiaGetUpdatedCache, as well as some UI Automation ConditionTypes.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 391 +++++++++++++++++++- dlls/uiautomationcore/uia_client.c | 11 + dlls/uiautomationcore/uiautomationcore.spec | 2 +- include/uiautomationcoreapi.h | 8 + 4 files changed, 409 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 62826430347..ae19fd4ce17 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1108,6 +1108,10 @@ static struct Provider const char *prov_name; IRawElementProviderFragment *parent; IRawElementProviderFragmentRoot *frag_root; + IRawElementProviderFragment *prev_sibling; + IRawElementProviderFragment *next_sibling; + IRawElementProviderFragment *first_child; + IRawElementProviderFragment *last_child; enum ProviderOptions prov_opts; HWND hwnd; BOOL ret_invalid_prop_type; @@ -1801,11 +1805,36 @@ static HRESULT WINAPI ProviderFragment_Navigate(IRawElementProviderFragment *ifa This->last_call_tid = GetCurrentThreadId();
*ret_val = NULL; - if ((direction == NavigateDirection_Parent) && This->parent) + switch (direction) { + case NavigateDirection_Parent: *ret_val = This->parent; - IRawElementProviderFragment_AddRef(This->parent); + break; + + case NavigateDirection_NextSibling: + *ret_val = This->next_sibling; + break; + + case NavigateDirection_PreviousSibling: + *ret_val = This->prev_sibling; + break; + + case NavigateDirection_FirstChild: + *ret_val = This->first_child; + break; + + case NavigateDirection_LastChild: + *ret_val = This->last_child; + break; + + default: + trace("Invalid navigate direction %d\n", direction); + break; } + + if (*ret_val) + IRawElementProviderFragment_AddRef(*ret_val); + return S_OK; }
@@ -1993,6 +2022,8 @@ static struct Provider Provider = 1, "Provider", NULL, NULL, + NULL, NULL, + &Provider_child.IRawElementProviderFragment_iface, &Provider_child2.IRawElementProviderFragment_iface, 0, 0, 0, };
@@ -2005,6 +2036,8 @@ static struct Provider Provider2 = 1, "Provider2", NULL, NULL, + NULL, NULL, + NULL, NULL, 0, 0, 0, };
@@ -2017,6 +2050,8 @@ static struct Provider Provider_child = 1, "Provider_child", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, + NULL, &Provider_child2.IRawElementProviderFragment_iface, + NULL, NULL, ProviderOptions_ServerSideProvider, 0, 0, };
@@ -2029,6 +2064,8 @@ static struct Provider Provider_child2 = 1, "Provider_child2", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, + &Provider_child.IRawElementProviderFragment_iface, NULL, + NULL, NULL, ProviderOptions_ServerSideProvider, 0, 0, };
@@ -2041,6 +2078,8 @@ static struct Provider Provider_hwnd = 1, "Provider_hwnd", NULL, NULL, + NULL, NULL, + NULL, NULL, ProviderOptions_ClientSideProvider, 0, 0, };
@@ -2053,6 +2092,8 @@ static struct Provider Provider_nc = 1, "Provider_nc", NULL, NULL, + NULL, NULL, + NULL, NULL, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, 0, 0, }; @@ -2066,6 +2107,8 @@ static struct Provider Provider_proxy = 1, "Provider_proxy", NULL, NULL, + NULL, NULL, + NULL, NULL, ProviderOptions_ClientSideProvider, 0, 0, }; @@ -2079,6 +2122,8 @@ static struct Provider Provider_proxy2 = 1, "Provider_proxy2", NULL, NULL, + NULL, NULL, + NULL, NULL, ProviderOptions_ClientSideProvider, 0, 0, }; @@ -2092,6 +2137,8 @@ static struct Provider Provider_override = 1, "Provider_override", NULL, NULL, + NULL, NULL, + NULL, NULL, ProviderOptions_ServerSideProvider | ProviderOptions_OverrideProvider, 0, 0, }; @@ -6671,6 +6718,345 @@ static void test_UiaRegisterProviderCallback(void) UnregisterClassA("UiaRegisterProviderCallback child class", NULL); }
+static void set_cache_request(struct UiaCacheRequest *req, struct UiaCondition *view_cond, int scope, + PROPERTYID *prop_ids, int prop_ids_count, PATTERNID *pattern_ids, int pattern_ids_count, int elem_mode) +{ + req->pViewCondition = view_cond; + req->Scope = scope; + + req->pProperties = prop_ids; + req->cProperties = prop_ids_count; + req->pPatterns = pattern_ids; + req->cPatterns = pattern_ids_count; + + req->automationElementMode = elem_mode; +} + +#define MAX_NODE_PROVIDERS 4 +struct node_provider_desc { + DWORD pid; + HWND hwnd; + + const WCHAR *prov_type[MAX_NODE_PROVIDERS]; + const WCHAR *prov_name[MAX_NODE_PROVIDERS]; + BOOL parent_link[MAX_NODE_PROVIDERS]; + int prov_count; +}; + +static void init_node_provider_desc(struct node_provider_desc *desc, DWORD pid, HWND hwnd) +{ + memset(desc, 0, sizeof(*desc)); + desc->pid = pid; + desc->hwnd = hwnd; +} + +static void add_provider_desc(struct node_provider_desc *desc, const WCHAR *prov_type, const WCHAR *prov_name, + BOOL parent_link) +{ + desc->prov_type[desc->prov_count] = prov_type; + desc->prov_name[desc->prov_count] = prov_name; + desc->parent_link[desc->prov_count] = parent_link; + desc->prov_count++; +} + +#define test_node_provider_desc( desc, desc_str ) \ + test_node_provider_desc_( (desc), (desc_str), __FILE__, __LINE__) +static void test_node_provider_desc_(struct node_provider_desc *desc, BSTR desc_str, const char *file, int line) +{ + int i; + + check_node_provider_desc_prefix_(desc_str, desc->pid, desc->hwnd, file, line); + for (i = 0; i < desc->prov_count; i++) + check_node_provider_desc_(desc_str, desc->prov_type[i], desc->prov_name[i], desc->parent_link[i], file, line); +} + +/* + * Cache requests are returned as a two dimensional safearray, with the first + * dimension being the element index, and the second index being the + * node/property/pattern value index for the element. The first element value is + * always an HUIANODE, followed by requested property values, and finally + * requested pattern handles. + */ +#define test_cache_req_sa( sa, exp_lbound, exp_elems, exp_node_desc ) \ + test_cache_req_sa_( (sa), (exp_lbound), (exp_elems), (exp_node_desc), __FILE__, __LINE__) +static void test_cache_req_sa_(SAFEARRAY *sa, LONG exp_lbound[2], LONG exp_elems[2], + struct node_provider_desc *exp_node_desc, const char *file, int line) +{ + HUIANODE node; + HRESULT hr; + VARTYPE vt; + VARIANT v; + UINT dims; + int i; + + hr = SafeArrayGetVartype(sa, &vt); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(vt == VT_VARIANT, "Unexpected vt %d\n", vt); + + dims = SafeArrayGetDim(sa); + ok_(file, line)(dims == 2, "Unexpected dims %d\n", dims); + + for (i = 0; i < 2; i++) + { + LONG lbound, ubound, elems; + + lbound = ubound = elems = 0; + hr = SafeArrayGetLBound(sa, 1 + i, &lbound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetLBound\n", hr); + ok_(file, line)(exp_lbound[i] == lbound, "Unexpected lbound[%d] %ld\n", i, lbound); + + hr = SafeArrayGetUBound(sa, 1 + i, &ubound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetUBound\n", hr); + + elems = (ubound - lbound) + 1; + ok_(file, line)(exp_elems[i] == elems, "Unexpected elems[%d] %ld\n", i, elems); + } + + for (i = 0; i < exp_elems[0]; i++) + { + LONG idx[2] = { (exp_lbound[0] + i), exp_lbound[1] }; + + hr = SafeArrayGetElement(sa, idx, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + + hr = UiaHUiaNodeFromVariant(&v, &node); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + VariantClear(&v); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + test_node_provider_desc_(&exp_node_desc[i], V_BSTR(&v), file, line); + VariantClear(&v); + + UiaNodeRelease(node); + } +} + +/* + * This sequence of method calls is always used when creating an HUIANODE from + * an IRawElementProviderSimple. + */ +#define NODE_CREATE_SEQ(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ + { prov , PROV_GET_PROPERTY_VALUE }, \ + { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + +static const struct prov_method_sequence cache_req_seq1[] = { + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId. */ + { 0 } +}; + +/* + * Win10v1507 and below will attempt to do parent navigation if the + * conditional check fails. + */ +static const struct prov_method_sequence cache_req_seq2[] = { + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq3[] = { + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + /* Navigates towards parent to check for clientside provider siblings. */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId. */ + { &Provider_child2, 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) +{ + struct node_provider_desc exp_node_desc[2]; + LONG exp_lbound[2], exp_elems[2], i; + struct UiaCacheRequest cache_req; + SAFEARRAY *out_req; + BSTR tree_struct; + HUIANODE node; + HRESULT hr; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + Provider.prov_opts = ProviderOptions_ServerSideProvider; + Provider.hwnd = NULL; + hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &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", TRUE); + VariantClear(&v); + } + + ok_method_sequence(node_from_prov2, NULL); + + /* NULL arg tests. */ + set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, NULL); + todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = UiaGetUpdatedCache(node, NULL, NormalizeState_None, NULL, &out_req, &tree_struct); + todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = UiaGetUpdatedCache(NULL, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* + * Cache request with NULL view condition, doesn't matter with + * NormalizeState_None as passed in HUIANODE isn't evaluated against any + * condition. + */ + tree_struct = NULL; out_req = NULL; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1"); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + /* + * NormalizeState_View, HUIANODE gets checked against the ConditionType_False + * condition within the cache request structure, nothing is returned. + */ + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaFalseCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + /* Empty tree structure string. */ + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + ok_method_sequence(cache_req_seq2, "cache_req_seq2"); + + /* + * NormalizeState_View, HUIANODE gets checked against the ConditionType_True + * condition within the cache request structure, returns this HUIANODE. + */ + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1"); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + /* + * NormalizeState_Custom, HUIANODE gets checked against our passed in + * ConditionType_False condition. + */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_Custom, (struct UiaCondition *)&UiaFalseCondition, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + /* Empty tree structure string. */ + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + + ok_method_sequence(cache_req_seq2, "cache_req_seq2"); + + /* + * NormalizeState_Custom, HUIANODE gets checked against the ConditionType_True + * condition we pass in, returns this HUIANODE. + */ + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_Custom, (struct UiaCondition *)&UiaTrueCondition, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1"); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + /* + * CacheRequest with TreeScope_Children. + */ + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); + tree_struct = NULL; out_req = NULL; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Children, NULL, 0, NULL, 0, AutomationElementMode_Full); + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + if (out_req) + { + exp_elems[0] = 2; + exp_elems[1] = 1; + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + ok(!wcscmp(tree_struct, L"(P)P))"), "tree structure %s\n", debugstr_w(tree_struct)); + ok_method_sequence(cache_req_seq3, "cache_req_seq3"); + } + + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + + CoUninitialize(); +} + /* * Once a process returns a UI Automation provider with * UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This @@ -6734,6 +7120,7 @@ START_TEST(uiautomation) test_UiaHUiaNodeFromVariant(); launch_test_process(argv[0], "UiaNodeFromHandle"); launch_test_process(argv[0], "UiaRegisterProviderCallback"); + test_UiaGetUpdatedCache(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible"); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 05f7c027565..e16851e55e4 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1911,3 +1911,14 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback) else uia_provider_callback = default_uia_provider_callback; } + +/*********************************************************************** + * UiaGetUpdatedCache (uiautomationcore.@) + */ +HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state, + struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct) +{ + FIXME("(%p, %p, %u, %p, %p, %p): stub\n", huianode, cache_req, normalize_state, normalize_cond, out_req, + tree_struct); + return E_NOTIMPL; +} diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec index 03a747219ed..56d1d33bd2b 100644 --- a/dlls/uiautomationcore/uiautomationcore.spec +++ b/dlls/uiautomationcore/uiautomationcore.spec @@ -67,7 +67,7 @@ @ stdcall UiaGetReservedNotSupportedValue(ptr) @ stub UiaGetRootNode @ stdcall UiaGetRuntimeId(ptr ptr) -@ stub UiaGetUpdatedCache +@ stdcall UiaGetUpdatedCache(ptr ptr long ptr ptr ptr) @ stub UiaHPatternObjectFromVariant @ stub UiaHTextRangeFromVariant @ stdcall UiaHUiaNodeFromVariant(ptr ptr) diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h index 025b811ba5e..ecac43b7c01 100644 --- a/include/uiautomationcoreapi.h +++ b/include/uiautomationcoreapi.h @@ -324,6 +324,12 @@ enum AsyncContentLoadedState { AsyncContentLoadedState_Completed = 0x02, };
+enum NormalizeState { + NormalizeState_None = 0x00, + NormalizeState_View = 0x01, + NormalizeState_Custom = 0x02, +}; + struct UiaEventArgs { enum EventArgsType Type; int EventId; @@ -399,6 +405,8 @@ HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id); HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode); HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode); HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov); +HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state, + struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct);
#ifdef __cplusplus }
From: Connor McAdams cmcadams@codeweavers.com
Implement UiaGetUpdatedCache for non-conditional, TreeScope_Element cache requests.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 26 +++++----- dlls/uiautomationcore/uia_client.c | 58 ++++++++++++++++++++-- 2 files changed, 67 insertions(+), 17 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ae19fd4ce17..136f5a08dcf 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6912,13 +6912,13 @@ static void test_UiaGetUpdatedCache(void) /* NULL arg tests. */ set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, NULL); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
hr = UiaGetUpdatedCache(node, NULL, NormalizeState_None, NULL, &out_req, &tree_struct); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
hr = UiaGetUpdatedCache(NULL, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* * Cache request with NULL view condition, doesn't matter with @@ -6929,17 +6929,15 @@ static void test_UiaGetUpdatedCache(void) add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_None, NULL, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (out_req) - { - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; - 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)); - ok_method_sequence(cache_req_seq1, "cache_req_seq1"); - } + 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] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1");
SafeArrayDestroy(out_req); SysFreeString(tree_struct); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index e16851e55e4..2bb418e8086 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1918,7 +1918,59 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback) HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state, struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct) { - FIXME("(%p, %p, %u, %p, %p, %p): stub\n", huianode, cache_req, normalize_state, normalize_cond, out_req, - tree_struct); - return E_NOTIMPL; + struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); + SAFEARRAYBOUND sabound[2]; + SAFEARRAY *sa; + LONG idx[2]; + HRESULT hr; + VARIANT v; + + TRACE("(%p, %p, %u, %p, %p, %p)\n", huianode, cache_req, normalize_state, normalize_cond, out_req, tree_struct); + + if (!node || !out_req || !tree_struct || !cache_req) + return E_INVALIDARG; + + *tree_struct = NULL; + *out_req = NULL; + + if (normalize_state != NormalizeState_None) + { + FIXME("Unsupported NormalizeState %d\n", normalize_state); + return E_NOTIMPL; + } + + if (cache_req->Scope != TreeScope_Element) + { + FIXME("Unsupported cache request scope %#x\n", cache_req->Scope); + return E_NOTIMPL; + } + + sabound[0].cElements = sabound[1].cElements = 1; + sabound[0].lLbound = sabound[1].lLbound = 0; + if (!(sa = SafeArrayCreate(VT_VARIANT, 2, sabound))) + { + WARN("Failed to create safearray\n"); + return E_FAIL; + } + + get_variant_for_node(huianode, &v); + idx[0] = idx[1] = 0; + + hr = SafeArrayPutElement(sa, idx, &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. + */ + IWineUiaNode_AddRef(&node->IWineUiaNode_iface); + + *out_req = sa; + *tree_struct = SysAllocString(L"P)"); + + return S_OK; }
From: Connor McAdams cmcadams@codeweavers.com
Add support for ConditionType_{True/False}.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 56 +++++++++--------- dlls/uiautomationcore/uia_client.c | 68 ++++++++++++++++++++-- 2 files changed, 89 insertions(+), 35 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 136f5a08dcf..96c2c128906 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6949,12 +6949,12 @@ static void test_UiaGetUpdatedCache(void) tree_struct = NULL; out_req = NULL; set_cache_request(&cache_req, (struct UiaCondition *)&UiaFalseCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!out_req, "out_req != NULL\n"); + /* Empty tree structure string. */ - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (tree_struct) - ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); SysFreeString(tree_struct); ok_method_sequence(cache_req_seq2, "cache_req_seq2");
@@ -6965,17 +6965,15 @@ static void test_UiaGetUpdatedCache(void) tree_struct = NULL; out_req = NULL; set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (out_req) - { - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; - 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)); - ok_method_sequence(cache_req_seq1, "cache_req_seq1"); - } + 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] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1");
SafeArrayDestroy(out_req); SysFreeString(tree_struct); @@ -6987,12 +6985,12 @@ static void test_UiaGetUpdatedCache(void) set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); tree_struct = NULL; out_req = NULL; hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_Custom, (struct UiaCondition *)&UiaFalseCondition, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!out_req, "out_req != NULL\n"); + /* Empty tree structure string. */ - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (tree_struct) - ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); SysFreeString(tree_struct);
ok_method_sequence(cache_req_seq2, "cache_req_seq2"); @@ -7004,17 +7002,15 @@ static void test_UiaGetUpdatedCache(void) tree_struct = NULL; out_req = NULL; set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_Custom, (struct UiaCondition *)&UiaTrueCondition, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (out_req) - { - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; - 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)); - ok_method_sequence(cache_req_seq1, "cache_req_seq1"); - } + 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] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, "cache_req_seq1");
SafeArrayDestroy(out_req); SysFreeString(tree_struct); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 2bb418e8086..12b53891dcd 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1912,6 +1912,37 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback) uia_provider_callback = default_uia_provider_callback; }
+static BOOL uia_condition_matched(HRESULT hr) +{ + if (hr == S_FALSE) + return FALSE; + else + return TRUE; +} + +static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) +{ + switch (condition->ConditionType) + { + case ConditionType_True: + return S_OK; + + case ConditionType_False: + return S_FALSE; + + case ConditionType_Property: + case ConditionType_And: + case ConditionType_Or: + case ConditionType_Not: + FIXME("Unhandled condition type %d\n", condition->ConditionType); + return E_NOTIMPL; + + default: + WARN("Invalid condition type %d\n", condition->ConditionType); + return E_INVALIDARG; + } +} + /*********************************************************************** * UiaGetUpdatedCache (uiautomationcore.@) */ @@ -1919,6 +1950,7 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct) { struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); + struct UiaCondition *cond; SAFEARRAYBOUND sabound[2]; SAFEARRAY *sa; LONG idx[2]; @@ -1933,16 +1965,42 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac *tree_struct = NULL; *out_req = NULL;
- if (normalize_state != NormalizeState_None) + if (cache_req->Scope != TreeScope_Element) { - FIXME("Unsupported NormalizeState %d\n", normalize_state); + FIXME("Unsupported cache request scope %#x\n", cache_req->Scope); return E_NOTIMPL; }
- if (cache_req->Scope != TreeScope_Element) + switch (normalize_state) { - FIXME("Unsupported cache request scope %#x\n", cache_req->Scope); - return E_NOTIMPL; + case NormalizeState_None: + cond = NULL; + break; + + case NormalizeState_View: + cond = cache_req->pViewCondition; + break; + + case NormalizeState_Custom: + cond = normalize_cond; + break; + + default: + WARN("Invalid normalize_state %d\n", normalize_state); + return E_INVALIDARG; + } + + if (cond) + { + hr = uia_condition_check(huianode, cond); + if (FAILED(hr)) + return hr; + + if (!uia_condition_matched(hr)) + { + *tree_struct = SysAllocString(L""); + return S_OK; + } }
sabound[0].cElements = sabound[1].cElements = 1;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 96c2c128906..972fd33e499 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1097,6 +1097,12 @@ static struct Accessible Accessible_child2 = FALSE, 0, };
+struct Provider_prop_override +{ + int prop_id; + VARIANT val; +}; + static struct Provider { IRawElementProviderSimple IRawElementProviderSimple_iface; @@ -1120,6 +1126,8 @@ static struct Provider DWORD last_call_tid; BOOL ignore_hwnd_prop; HWND override_hwnd; + struct Provider_prop_override *prop_override; + int prop_override_count; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override;
@@ -1563,6 +1571,20 @@ HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface, ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); This->last_call_tid = GetCurrentThreadId();
+ if (This->prop_override && This->prop_override_count) + { + int i; + + for (i = 0; i < This->prop_override_count; i++) + { + if (This->prop_override[i].prop_id == prop_id) + { + *ret_val = This->prop_override[i].val; + return S_OK; + } + } + } + VariantInit(ret_val); switch (prop_id) {
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 337 +++++++++++++++++++++ 1 file changed, 337 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 972fd33e499..e648820a51a 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6754,6 +6754,28 @@ static void set_cache_request(struct UiaCacheRequest *req, struct UiaCondition * req->automationElementMode = elem_mode; }
+static void set_property_condition(struct UiaPropertyCondition *cond, int prop_id, VARIANT *val, int flags) +{ + cond->ConditionType = ConditionType_Property; + cond->PropertyId = prop_id; + cond->Value = *val; + cond->Flags = flags; +} + +static void set_and_or_condition(struct UiaAndOrCondition *cond, int cond_type, + struct UiaCondition **conds, int cond_count) +{ + cond->ConditionType = cond_type; + cond->ppConditions = conds; + cond->cConditions = cond_count; +} + +static void set_not_condition(struct UiaNotCondition *cond, struct UiaCondition *not_cond) +{ + cond->ConditionType = ConditionType_Not; + cond->pConditions = not_cond; +} + #define MAX_NODE_PROVIDERS 4 struct node_provider_desc { DWORD pid; @@ -6896,13 +6918,42 @@ static const struct prov_method_sequence cache_req_seq3[] = { { 0 } };
+ +static const struct prov_method_sequence cache_req_seq4[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, /* Dependent upon property condition. */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId. */ + { 0 } +}; + +/* Sequence for non-matching property condition. */ +static const struct prov_method_sequence cache_req_seq5[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, /* Dependent upon property condition. */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* Dependent upon property condition. */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq6[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId. */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + static const struct UiaCondition UiaTrueCondition = { ConditionType_True }; static const struct UiaCondition UiaFalseCondition = { ConditionType_False }; static void test_UiaGetUpdatedCache(void) { + 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; SAFEARRAY *out_req; BSTR tree_struct; HUIANODE node; @@ -7067,6 +7118,292 @@ static void test_UiaGetUpdatedCache(void) SafeArrayDestroy(out_req); SysFreeString(tree_struct);
+ /* + * ConditionType_And tests. + */ + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + cond_arr[0] = (struct UiaCondition *)&UiaTrueCondition; + cond_arr[1] = (struct UiaCondition *)&UiaTrueCondition; + set_and_or_condition(&and_or_cond, ConditionType_And, cond_arr, ARRAY_SIZE(cond_arr)); + set_cache_request(&cache_req, (struct UiaCondition *)&and_or_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (1 && 1) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + cond_arr[0] = (struct UiaCondition *)&UiaTrueCondition; + cond_arr[1] = (struct UiaCondition *)&UiaFalseCondition; + set_and_or_condition(&and_or_cond, ConditionType_And, cond_arr, ARRAY_SIZE(cond_arr)); + set_cache_request(&cache_req, (struct UiaCondition *)&and_or_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (1 && 0) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + ok_method_sequence(cache_req_seq2, NULL); + + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + + /* + * ConditionType_Or tests. + */ + cond_arr[0] = (struct UiaCondition *)&UiaTrueCondition; + cond_arr[1] = (struct UiaCondition *)&UiaFalseCondition; + set_and_or_condition(&and_or_cond, ConditionType_Or, cond_arr, ARRAY_SIZE(cond_arr)); + set_cache_request(&cache_req, (struct UiaCondition *)&and_or_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (1 || 0) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + cond_arr[0] = (struct UiaCondition *)&UiaFalseCondition; + cond_arr[1] = (struct UiaCondition *)&UiaFalseCondition; + set_and_or_condition(&and_or_cond, ConditionType_Or, cond_arr, ARRAY_SIZE(cond_arr)); + set_cache_request(&cache_req, (struct UiaCondition *)&and_or_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (0 || 0) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + ok_method_sequence(cache_req_seq2, NULL); + + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + + /* + * ConditionType_Not tests. + */ + set_not_condition(¬_cond, (struct UiaCondition *)&UiaFalseCondition); + set_cache_request(&cache_req, (struct UiaCondition *)¬_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (!0) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + set_not_condition(¬_cond, (struct UiaCondition *)&UiaTrueCondition); + set_cache_request(&cache_req, (struct UiaCondition *)¬_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + /* Equivalent to: if (!1) */ + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + ok_method_sequence(cache_req_seq2, NULL); + + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + + /* + * ConditionType_Property tests. + */ + Provider.ret_invalid_prop_type = FALSE; + + /* Test UIAutomationType_IntArray property conditions. */ + if (UiaLookupId(AutomationIdentifierType_Property, &OutlineColor_Property_GUID)) + { + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = create_i4_safearray(); + set_property_condition(&prop_cond, UIA_OutlineColorPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq4, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + VariantClear(&v); + + /* Same values, except we're short by one element. */ + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = SafeArrayCreateVector(VT_I4, 0, ARRAY_SIZE(uia_i4_arr_prop_val) - 1); + + for (i = 0; i < ARRAY_SIZE(uia_i4_arr_prop_val) - 1; i++) + SafeArrayPutElement(V_ARRAY(&prop_cond.Value), &i, (void *)&uia_i4_arr_prop_val[i]); + + set_property_condition(&prop_cond, UIA_OutlineColorPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + ok_method_sequence(cache_req_seq5, NULL); + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + VariantClear(&v); + } + else + win_skip("UIA_OutlineColorPropertyId unavailable, skipping property condition tests for it.\n"); + + /* UIA_RuntimeIdPropertyId comparison. */ + Provider.runtime_id[0] = 0xdeadbeef; Provider.runtime_id[1] = 0xfeedbeef; + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = SafeArrayCreateVector(VT_I4, 0, ARRAY_SIZE(Provider.runtime_id)); + for (i = 0; i < ARRAY_SIZE(Provider.runtime_id); i++) + SafeArrayPutElement(V_ARRAY(&v), &i, (void *)&Provider.runtime_id[i]); + + set_property_condition(&prop_cond, UIA_RuntimeIdPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq6, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + VariantClear(&prop_cond.Value); + + /* UIAutomationType_Bool property condition tests. */ + prop_override.prop_id = UIA_IsControlElementPropertyId; + V_VT(&prop_override.val) = VT_BOOL; + V_BOOL(&prop_override.val) = VARIANT_FALSE; + Provider.prop_override = &prop_override; + Provider.prop_override_count = 1; + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (out_req) + { + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq4, NULL); + } + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + VariantClear(&prop_cond.Value); + + /* + * Provider now returns VARIANT_TRUE for UIA_IsControlElementPropertyId, + * conditional check will fail. + */ + prop_override.prop_id = UIA_IsControlElementPropertyId; + V_VT(&prop_override.val) = VT_BOOL; + V_BOOL(&prop_override.val) = VARIANT_TRUE; + Provider.prop_override = &prop_override; + Provider.prop_override_count = 1; + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + ok_method_sequence(cache_req_seq5, NULL); + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + VariantClear(&prop_cond.Value); + + Provider.prop_override = NULL; + Provider.prop_override_count = 0; + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 31 +++++++++------------- dlls/uiautomationcore/uia_client.c | 16 ++++++++++- 2 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index e648820a51a..77f01927900 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -7223,17 +7223,15 @@ static void test_UiaGetUpdatedCache(void)
/* Equivalent to: if (!0) */ hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (out_req) - { - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; - 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)); - ok_method_sequence(cache_req_seq1, NULL); - } + 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] = exp_elems[1] = 1; + 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)); + ok_method_sequence(cache_req_seq1, NULL);
SafeArrayDestroy(out_req); SysFreeString(tree_struct); @@ -7245,15 +7243,12 @@ static void test_UiaGetUpdatedCache(void)
/* Equivalent to: if (!1) */ hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - ok_method_sequence(cache_req_seq2, NULL); - + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!out_req, "out_req != NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (tree_struct) - ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); SysFreeString(tree_struct); + ok_method_sequence(cache_req_seq2, NULL);
/* * ConditionType_Property tests. diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 12b53891dcd..3897b036e09 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1930,10 +1930,24 @@ static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition case ConditionType_False: return S_FALSE;
+ case ConditionType_Not: + { + struct UiaNotCondition *not_cond = (struct UiaNotCondition *)condition; + HRESULT hr; + + hr = uia_condition_check(node, not_cond->pConditions); + if (FAILED(hr)) + return hr; + + if (uia_condition_matched(hr)) + return S_FALSE; + else + return S_OK; + } + case ConditionType_Property: case ConditionType_And: case ConditionType_Or: - case ConditionType_Not: FIXME("Unhandled condition type %d\n", condition->ConditionType); return E_NOTIMPL;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=125360
Your paranoid android.
=== debian11 (build log) ===
Task: Could not create the win32 wineprefix: Failed to disable the crash dialogs: Task: WineTest did not produce the win32 report
It seems like `uiautomationcore/tests: Add support for overriding default property return values for Providers.` adds only unused code?
On Mon Oct 24 19:28:16 2022 +0000, Esme Povirk wrote:
It seems like `uiautomationcore/tests: Add support for overriding default property return values for Providers.` adds only unused code?
Correct, it gets used in the next patch. I split it off to keep the new functionality from getting lost in the massive diff for the new tests, but I can squash them back together if that makes more sense.
I'd like to know a little bit more about how "tree structure" return will be implemented for scopes other then TreeScope_Element. I am not sure it makes sense to hard-code specific values.
On Mon Oct 24 21:21:44 2022 +0000, Esme Povirk wrote:
I'd like to know a little bit more about how "tree structure" return will be implemented for scopes other then TreeScope_Element. I am not sure it makes sense to hard-code specific values.
AFAICT, most things don't make use of cache requests with anything other than `TreeScope_Element`, which is why I've chosen to implement it this way. I intend to eventually use this in `UiaFind()`, and even there, when it's used from the managed API [here,](https://github.com/madewokherd/wpf/blob/06d2867166ba990f8ab8725d1c1081034270...) the TreeScope arguments get converted into depth values for `UiaFind()`.
If we wanted to actually implement TreeScopes other than `TreeScope_Element`, the behavior would be similar to how `UiaFind()` ends up functioning. We would just have the depth increment/decrements make changes to the returned tree structure string. But, like I said, I don't believe anything really uses the TreeScope variable inside of the cache request, and I had no intention of implementing anything other than `TreeScope_Element`.
On Mon Oct 24 21:21:44 2022 +0000, Connor McAdams wrote:
AFAICT, most things don't make use of cache requests with anything other than `TreeScope_Element`, which is why I've chosen to implement it this way. I intend to eventually use this in `UiaFind()`, and even there, when it's used from the managed API [here,](https://github.com/madewokherd/wpf/blob/06d2867166ba990f8ab8725d1c1081034270...) the TreeScope arguments get converted into depth values for `UiaFind()`. If we wanted to actually implement TreeScopes other than `TreeScope_Element`, the behavior would be similar to how `UiaFind()` ends up functioning. We would just have the depth increment/decrements make changes to the returned tree structure string. But, like I said, I don't believe anything really uses the TreeScope variable inside of the cache request, and I had no intention of implementing anything other than `TreeScope_Element`.
Well then, I guess it's fine as long as this implementation doesn't make it more difficult to add the remaining cases without breaking existing things.