From eeb098e7b6d4796216969ae284e47734ffedbd34 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Mon, 13 Mar 2023 11:54:22 -0400 Subject: [PATCH] uiautomationcore: Implement IUIAutomationElement::FindFirst{BuildCache}. Signed-off-by: Connor McAdams --- dlls/uiautomationcore/tests/uiautomation.c | 136 +++++++++------------ dlls/uiautomationcore/uia_com_client.c | 120 +++++++++++++++--- 2 files changed, 161 insertions(+), 95 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 5f6b4fd10aa..2a4f87c7b59 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -11418,62 +11418,53 @@ static void test_Element_Find(IUIAutomation *uia_iface) * root is TRUE. element2 now represents Provider_child. */ hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_Children, condition, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } - - ok_method_sequence(find_seq4, "find_seq4"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); } - if (element2) - { - /* - * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude - * root is FALSE. Provider_child doesn't have a runtime id for UI - * Automation to use as a way to check if it has navigated back to the - * node that began the search, so it will get siblings. - */ - hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 2); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); - set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); + ok_method_sequence(find_seq4, "find_seq4"); - test_uia_element_arr(element_arr, exp_elems, 2); - ok_method_sequence(find_seq5, "find_seq5"); - } + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child doesn't have a runtime id for UI + * Automation to use as a way to check if it has navigated back to the + * node that began the search, so it will get siblings. + */ + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - /* - * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude - * root is FALSE. Provider_child now has a runtime ID, so we don't get - * its sibling. - */ - Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; - hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); - IUIAutomationElement_Release(element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); - add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 2); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + set_elem_desc(&exp_elems[1], &Provider_child2, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[1].prov_desc, L"Main", L"Provider_child2", TRUE); - test_uia_element_arr(element_arr, exp_elems, 1); - ok_method_sequence(find_seq6, "find_seq6"); - } - initialize_provider_tree(FALSE); - } + test_uia_element_arr(element_arr, exp_elems, 2); + ok_method_sequence(find_seq5, "find_seq5"); + + /* + * Equivalent to: Maximum find depth of 0, find first is FALSE, exclude + * root is FALSE. Provider_child now has a runtime ID, so we don't get + * its sibling. + */ + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; + hr = IUIAutomationElement_FindAllBuildCache(element2, TreeScope_Element, condition, cache_req, &element_arr); + IUIAutomationElement_Release(element2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + set_elem_desc(&exp_elems[0], &Provider_child, NULL, GetCurrentProcessId(), 2, 1); + add_provider_desc(&exp_elems[0].prov_desc, L"Main", L"Provider_child", TRUE); + + test_uia_element_arr(element_arr, exp_elems, 1); + ok_method_sequence(find_seq6, "find_seq6"); + initialize_provider_tree(FALSE); IUIAutomationCondition_Release(condition); @@ -11618,22 +11609,20 @@ static void test_Element_Find(IUIAutomation *uia_iface) * to match our condition. */ hr = IUIAutomationElement_FindFirstBuildCache(element, TreeScope_SubTree, condition, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); - VariantClear(&v); - } - - IUIAutomationElement_Release(element2); - ok_method_sequence(find_seq11, "find_seq11"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); } + + IUIAutomationElement_Release(element2); + ok_method_sequence(find_seq11, "find_seq11"); initialize_provider_tree(FALSE); IUIAutomationCondition_Release(condition); @@ -11688,23 +11677,20 @@ static void test_Element_Find(IUIAutomation *uia_iface) set_provider_prop_override(&Provider_child_child, &prop_override, 1); hr = IUIAutomationElement_FindFirst(element, TreeScope_SubTree, condition, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ProviderDescriptionPropertyId, TRUE, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); - VariantClear(&v); - } - - IUIAutomationElement_Release(element2); - ok_method_sequence(element_find_seq2, "element_find_seq2"); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child_child2", TRUE); + VariantClear(&v); } + IUIAutomationElement_Release(element2); + ok_method_sequence(element_find_seq2, "element_find_seq2"); initialize_provider_tree(TRUE); IUIAutomationCondition_Release(condition); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 850bfb499e1..4b2e5cba741 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1110,8 +1110,23 @@ static HRESULT WINAPI uia_element_GetRuntimeId(IUIAutomationElement9 *iface, SAF static HRESULT WINAPI uia_element_FindFirst(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationElement **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + IUIAutomationCacheRequest *cache_req; + HRESULT hr; + + TRACE("%p, %#x, %p, %p\n", iface, scope, condition, found); + + if (!found) + return E_POINTER; + + *found = NULL; + hr = create_uia_cache_request_iface(&cache_req); + if (FAILED(hr)) + return hr; + + hr = IUIAutomationElement9_FindFirstBuildCache(iface, scope, condition, cache_req, found); + IUIAutomationCacheRequest_Release(cache_req); + + return hr; } static HRESULT WINAPI uia_element_FindAll(IUIAutomationElement9 *iface, enum TreeScope scope, @@ -1136,13 +1151,93 @@ static HRESULT WINAPI uia_element_FindAll(IUIAutomationElement9 *iface, enum Tre return hr; } +static HRESULT set_find_params_struct(struct UiaFindParams *params, IUIAutomationCondition *cond, int scope, + BOOL find_first) +{ + HRESULT hr; + + hr = get_uia_condition_struct_from_iface(cond, ¶ms->pFindCondition); + if (FAILED(hr)) + return hr; + + if (!scope || (scope & (~TreeScope_SubTree))) + return E_INVALIDARG; + + params->FindFirst = find_first; + if (scope & TreeScope_Element) + params->ExcludeRoot = FALSE; + else + params->ExcludeRoot = TRUE; + + if (scope & TreeScope_Descendants) + params->MaxDepth = -1; + else if (scope & TreeScope_Children) + params->MaxDepth = 1; + else + params->MaxDepth = 0; + + return S_OK; +} + static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_FindFirstBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **found) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + struct uia_element *element = impl_from_IUIAutomationElement9(iface); + LONG lbound_offsets, lbound_tree_structs, elems_count, offset_idx; + struct UiaFindParams find_params = { 0 }; + struct UiaCacheRequest *cache_req_struct; + SAFEARRAY *sa, *tree_structs, *offsets; + IUIAutomationElement *elem; + BSTR tree_struct_str; + HRESULT hr; + + TRACE("%p, %#x, %p, %p, %p\n", iface, scope, condition, cache_req, found); + + if (!found) + return E_POINTER; + + *found = elem = NULL; + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + return hr; + + hr = set_find_params_struct(&find_params, condition, scope, TRUE); + if (FAILED(hr)) + return hr; + + sa = offsets = tree_structs = NULL; + hr = UiaFind(element->node, &find_params, cache_req_struct, &sa, &offsets, &tree_structs); + if (FAILED(hr) || !sa) + goto exit; + + hr = get_safearray_bounds(tree_structs, &lbound_tree_structs, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayGetElement(tree_structs, &lbound_tree_structs, &tree_struct_str); + if (FAILED(hr)) + goto exit; + + hr = get_safearray_bounds(offsets, &lbound_offsets, &elems_count); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayGetElement(offsets, &lbound_offsets, &offset_idx); + if (FAILED(hr)) + goto exit; + + hr = create_uia_element_from_cache_req(&elem, element->from_cui8, cache_req_struct, offset_idx, sa, tree_struct_str); + if (SUCCEEDED(hr)) + *found = elem; + +exit: + SafeArrayDestroy(tree_structs); + SafeArrayDestroy(offsets); + SafeArrayDestroy(sa); + + return hr; } static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, @@ -1168,25 +1263,10 @@ static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface if (FAILED(hr)) return hr; - hr = get_uia_condition_struct_from_iface(condition, &find_params.pFindCondition); + hr = set_find_params_struct(&find_params, condition, scope, FALSE); if (FAILED(hr)) return hr; - if (!scope || (scope & (~TreeScope_SubTree))) - return E_INVALIDARG; - - if (scope & TreeScope_Element) - find_params.ExcludeRoot = FALSE; - else - find_params.ExcludeRoot = TRUE; - - if (scope & TreeScope_Descendants) - find_params.MaxDepth = -1; - else if (scope & TreeScope_Children) - find_params.MaxDepth = 1; - else - find_params.MaxDepth = 0; - sa = offsets = tree_structs = NULL; hr = UiaFind(element->node, &find_params, cache_req_struct, &sa, &offsets, &tree_structs); if (FAILED(hr) || !sa)