From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 86 ++++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 29 ++++++-- 2 files changed, 108 insertions(+), 7 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 8ab80a9b89d..55dbaf8eb67 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3002,6 +3002,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; @@ -3053,16 +3054,29 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac return E_INVALIDARG; }
+ ret_node = huianode; if (cond) { hr = uia_condition_check(huianode, cond); if (FAILED(hr)) return hr;
+ /* + * Attempt to get the nearest ancestor matching the passed in + * condition. + */ if (!uia_condition_matched(hr)) { - *tree_struct = SysAllocString(L""); - return S_OK; + ret_node = NULL; + hr = conditional_navigate_uia_node(node, NavigateDirection_Parent, cond, &ret_node); + if (FAILED(hr)) + return hr; + + if (!ret_node) + { + *tree_struct = SysAllocString(L""); + return S_OK; + } } }
@@ -3075,7 +3089,7 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac return E_FAIL; }
- get_variant_for_node(huianode, &v); + get_variant_for_node(ret_node, &v); idx[0] = idx[1] = 0;
hr = SafeArrayPutElement(sa, idx, &v); @@ -3089,7 +3103,7 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac 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) { @@ -3108,10 +3122,11 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac }
/* - * AddRef huianode since we're returning a reference to the same node we - * passed in, rather than creating a new one. + * If we're returning a reference to the same node we passed in, make sure + * to AddRef it. */ - IWineUiaNode_AddRef(&node->IWineUiaNode_iface); + if (ret_node == huianode) + IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
*out_req = sa; *tree_struct = SysAllocString(L"P)");