From cd9f3e5d05b3cd6b7f71e4a8337df55531651ff4 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Fri, 24 Mar 2023 11:29:16 -0400 Subject: [PATCH] uiautomationcore: Add normalization navigation to UiaGetUpdatedCache. Signed-off-by: Connor McAdams --- dlls/uiautomationcore/tests/uiautomation.c | 86 ++++++++++++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 80 ++++++++++++++++----------- 2 files changed, 134 insertions(+), 32 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 193f1470029..40f6cdd9f38 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -7873,6 +7873,34 @@ static const struct prov_method_sequence cache_req_seq7[] = { { 0 } }; +static const struct prov_method_sequence cache_req_seq8[] = { + NODE_CREATE_SEQ(&Provider_child), + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq9[] = { + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Done twice on Windows, but we shouldn't need to replicate this. */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq10[] = { + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Done twice on Windows, but we shouldn't need to replicate this. */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId. */ + { 0 } +}; + static const struct UiaCondition UiaTrueCondition = { ConditionType_True }; static const struct UiaCondition UiaFalseCondition = { ConditionType_False }; static void test_UiaGetUpdatedCache(void) @@ -8419,7 +8447,65 @@ static void test_UiaGetUpdatedCache(void) ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + + /* Normalization navigation tests. */ initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); + + hr = UiaNodeFromProvider(&Provider_child.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok_method_sequence(cache_req_seq8, "cache_req_seq8"); + + /* + * Neither Provider_child or Provider match this condition, return + * nothing. + */ + variant_init_bool(&v, 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + 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_seq9, "cache_req_seq9"); + + /* + * Provider now matches our condition, we'll get Provider in the cache + * request. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + 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_seq10, "cache_req_seq10"); + + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + VariantClear(&v); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); IUnknown_Release(unk_ns); CoUninitialize(); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 9b3d734defd..64fd8b3b1cb 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2992,6 +2992,31 @@ static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition } } +static HRESULT uia_node_normalize(HUIANODE huianode, struct UiaCondition *cond, HUIANODE *normalized_node) +{ + struct uia_node *node = impl_from_IWineUiaNode((IWineUiaNode *)huianode); + HRESULT hr; + + *normalized_node = NULL; + if (cond) + { + hr = uia_condition_check(huianode, cond); + if (FAILED(hr)) + return hr; + + /* + * If our initial node doesn't match our normalization condition, need + * to get the nearest ancestor that does. + */ + if (!uia_condition_matched(hr)) + return conditional_navigate_uia_node(node, NavigateDirection_Parent, cond, normalized_node); + } + + *normalized_node = huianode; + IWineUiaNode_AddRef(&node->IWineUiaNode_iface); + return S_OK; +} + /*********************************************************************** * UiaGetUpdatedCache (uiautomationcore.@) */ @@ -3001,6 +3026,7 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); struct UiaCondition *cond; SAFEARRAYBOUND sabound[2]; + HUIANODE ret_node; SAFEARRAY *sa; LONG idx[2]; HRESULT hr; @@ -3052,17 +3078,14 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac return E_INVALIDARG; } - if (cond) - { - hr = uia_condition_check(huianode, cond); - if (FAILED(hr)) - return hr; + hr = uia_node_normalize(huianode, cond, &ret_node); + if (FAILED(hr)) + return hr; - if (!uia_condition_matched(hr)) - { - *tree_struct = SysAllocString(L""); - return S_OK; - } + if (!ret_node) + { + *tree_struct = SysAllocString(L""); + return S_OK; } sabound[0].cElements = 1; @@ -3071,51 +3094,44 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac if (!(sa = SafeArrayCreate(VT_VARIANT, 2, sabound))) { WARN("Failed to create safearray\n"); - return E_FAIL; + hr = E_FAIL; + goto exit; } - get_variant_for_node(huianode, &v); + get_variant_for_node(ret_node, &v); idx[0] = idx[1] = 0; hr = SafeArrayPutElement(sa, idx, &v); if (FAILED(hr)) - { - SafeArrayDestroy(sa); - return hr; - } + goto exit; idx[0] = 0; VariantClear(&v); for (i = 0; i < cache_req->cProperties; i++) { - hr = UiaGetPropertyValue(huianode, cache_req->pProperties[i], &v); + hr = UiaGetPropertyValue(ret_node, cache_req->pProperties[i], &v); /* Don't fail on unimplemented properties. */ if (FAILED(hr) && hr != E_NOTIMPL) - { - SafeArrayDestroy(sa); - return hr; - } + goto exit; idx[1] = 1 + i; hr = SafeArrayPutElement(sa, idx, &v); VariantClear(&v); if (FAILED(hr)) - { - SafeArrayDestroy(sa); - return hr; - } + goto exit; } - /* - * 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; +exit: + if (FAILED(hr)) + { + SafeArrayDestroy(sa); + UiaNodeRelease(ret_node); + } + + return hr; } /*********************************************************************** -- 2.11.4.GIT