[PATCH 0/6] MR1150: uiautomationcore: Implement UiaGetUpdatedCache.
Implement UiaGetUpdatedCache, as well as some UI Automation ConditionTypes. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)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 } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Implement UiaGetUpdatedCache for non-conditional, TreeScope_Element cache requests. Signed-off-by: Connor McAdams <cmcadams(a)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; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Add support for ConditionType_{True/False}. Signed-off-by: Connor McAdams <cmcadams(a)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; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)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) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)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); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)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; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/1150
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? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150#note_11922
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.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150#note_11923
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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150#note_11925
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`. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150#note_11970
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.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/1150#note_11971
participants (4)
-
Connor McAdams -
Connor McAdams (@cmcadams) -
Esme Povirk (@madewokherd) -
Marvin