-- v4: uiautomationcore: Add support for returning multiple HUIANODEs from UiaFind. uiautomationcore: Store traverse_uia_node_tree() results in a dynamic array. uiautomationcore: Add support for ExcludeRoot parameter in UiaFind. uiautomationcore: Partially implement UiaFind. uiautomationcore: Add UiaFind stub.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 1007 +++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 10 + dlls/uiautomationcore/uiautomationcore.spec | 2 +- include/uiautomationcoreapi.h | 9 + 4 files changed, 1027 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 827c23e26ef..f4767731ec3 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -2184,6 +2184,10 @@ DEFINE_PROVIDER(hwnd_child); DEFINE_PROVIDER(hwnd_child2); DEFINE_PROVIDER(nc_child); DEFINE_PROVIDER(nc_child2); +DEFINE_PROVIDER(child_child); +DEFINE_PROVIDER(child_child2); +DEFINE_PROVIDER(child2_child); +DEFINE_PROVIDER(child2_child_child);
static IAccessible *acc_client; static IRawElementProviderSimple *prov_root; @@ -8203,6 +8207,1008 @@ static void test_UiaNavigate(void) UnregisterClassA("UiaNavigate class", NULL); }
+static void set_find_params(struct UiaFindParams *params, int max_depth, BOOL find_first, BOOL exclude_root, + struct UiaCondition *find_cond) +{ + params->MaxDepth = max_depth; + params->FindFirst = find_first; + params->ExcludeRoot = exclude_root; + params->pFindCondition = find_cond; +} + +static void set_provider_prop_override(struct Provider *prov, struct Provider_prop_override *override, int count) +{ + prov->prop_override = override; + prov->prop_override_count = count; +} + +static void set_property_override(struct Provider_prop_override *override, int prop_id, VARIANT *val) +{ + override->prop_id = prop_id; + override->val = *val; +} + +static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links) +{ + prov->prov_opts = prov_opts; + prov->hwnd = hwnd; + prov->ret_invalid_prop_type = FALSE; + prov->expected_tid = 0; + prov->runtime_id[0] = prov->runtime_id[1] = 0; + prov->last_call_tid = 0; + prov->ignore_hwnd_prop = FALSE; + prov->override_hwnd = NULL; + prov->prop_override = NULL; + prov->prop_override_count = 0; + if (initialize_nav_links) + { + prov->frag_root = NULL; + prov->parent = prov->prev_sibling = prov->next_sibling = prov->first_child = prov->last_child = NULL; + } +} + +static void initialize_provider_tree(BOOL initialize_nav_links) +{ + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child_child, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child_child2, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child2_child, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); + initialize_provider(&Provider_child2_child_child, ProviderOptions_ServerSideProvider, NULL, initialize_nav_links); +} + +static void provider_add_child(struct Provider *prov, struct Provider *child) +{ + if (!prov->first_child) + { + prov->first_child = prov->last_child = &child->IRawElementProviderFragment_iface; + child->next_sibling = child->prev_sibling = NULL; + } + else + { + struct Provider *tmp = impl_from_ProviderFragment(prov->last_child); + + tmp->next_sibling = &child->IRawElementProviderFragment_iface; + child->prev_sibling = prov->last_child; + prov->last_child = &child->IRawElementProviderFragment_iface; + } + + child->parent = &prov->IRawElementProviderFragment_iface; + child->frag_root = prov->frag_root; +} + +#define test_find_sa_results( tree_structs, offsets, exp_elems, exp_tree_struct, exp_offsets ) \ + test_find_sa_results_( (tree_structs), (offsets), (exp_elems), (exp_tree_struct), (exp_offsets), __FILE__, __LINE__) +static void test_find_sa_results_(SAFEARRAY *tree_structs, SAFEARRAY *offsets, LONG exp_elems, + const WCHAR **exp_tree_struct, int *exp_offset, const char *file, int line) +{ + LONG lbound, ubound, elems; + HRESULT hr; + VARTYPE vt; + UINT dims; + LONG i; + + /* Tree structures SA. */ + hr = SafeArrayGetVartype(tree_structs, &vt); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(vt == VT_BSTR, "Unexpected tree structures sa vt %d\n", vt); + dims = SafeArrayGetDim(tree_structs); + ok_(file, line)(dims == 1, "Unexpected tree structures sa dims %d\n", dims); + + lbound = ubound = elems = 0; + hr = SafeArrayGetLBound(tree_structs, 1, &lbound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetLBound\n", hr); + ok_(file, line)(!lbound, "Unexpected lbound %ld\n", lbound); + + hr = SafeArrayGetUBound(tree_structs, 1, &ubound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetUBound\n", hr); + + elems = (ubound - lbound) + 1; + ok_(file, line)(exp_elems == elems, "Unexpected elems %ld\n", elems); + + /* Offsets SA. */ + hr = SafeArrayGetVartype(offsets, &vt); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(vt == VT_I4, "Unexpected offsets sa vt %d\n", vt); + dims = SafeArrayGetDim(offsets); + ok_(file, line)(dims == 1, "Unexpected offsets sa dims %d\n", dims); + + lbound = ubound = elems = 0; + hr = SafeArrayGetLBound(offsets, 1, &lbound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetLBound\n", hr); + ok_(file, line)(!lbound, "Unexpected lbound %ld\n", lbound); + + hr = SafeArrayGetUBound(offsets, 1, &ubound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetUBound\n", hr); + + elems = (ubound - lbound) + 1; + ok_(file, line)(exp_elems == elems, "Unexpected elems %ld\n", elems); + + for (i = 0; i < exp_elems; i++) + { + BSTR tree_struct; + int offset; + + hr = SafeArrayGetElement(tree_structs, &i, &tree_struct); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok_(file, line)(!wcscmp(tree_struct, exp_tree_struct[i]), "Unexpected tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + + hr = SafeArrayGetElement(offsets, &i, &offset); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok_(file, line)(exp_offset[i] == offset, "Unexpected offset %d\n", offset); + } +} + +static const struct prov_method_sequence find_seq1[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child2_child_child), + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq2[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &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 */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq3[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &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 */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq4[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq5[] = { + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &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 prov_method_sequence find_seq6[] = { + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq7[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq8[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq9[] = { + { &Provider_child_child2, FRAG_GET_RUNTIME_ID }, + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq10[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child2_child_child), + { &Provider_child2_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static const struct prov_method_sequence find_seq11[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_FirstChild */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_NextSibling */ + NODE_CREATE_SEQ(&Provider_child_child2), + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsContentElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 }, +}; + +static void test_UiaFind(void) +{ + struct Provider_prop_override prop_override; + LONG exp_lbound[2], exp_elems[2], idx[2], i; + SAFEARRAY *out_req, *offsets, *tree_structs; + struct node_provider_desc exp_node_desc[7]; + struct UiaPropertyCondition prop_cond[2]; + struct UiaCacheRequest cache_req; + struct UiaFindParams find_params; + const WCHAR *exp_tree_struct[7]; + HUIANODE node, node2; + int exp_offset[6]; + HRESULT hr; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + initialize_provider_tree(TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + provider_add_child(&Provider, &Provider_child); + provider_add_child(&Provider, &Provider_child2); + provider_add_child(&Provider_child, &Provider_child_child); + provider_add_child(&Provider_child, &Provider_child_child2); + provider_add_child(&Provider_child2, &Provider_child2_child); + provider_add_child(&Provider_child2_child, &Provider_child2_child_child); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), 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); + + /* + * Maximum find depth of -1, find first is FALSE, exclude root is FALSE. A + * maximum depth of -1 will search the entire tree. + */ + out_req = offsets = tree_structs = NULL; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, -1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + 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); + todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref); + todo_wine ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[4], L"Main", L"Provider_child2", TRUE); + add_provider_desc(&exp_node_desc[5], L"Main", L"Provider_child2_child", TRUE); + add_provider_desc(&exp_node_desc[6], L"Main", L"Provider_child2_child_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 7; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq1, "find_seq1"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 1, find first is FALSE, exclude root is FALSE. + */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + 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); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 3; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq2, "find_seq2"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 1, find first is FALSE, exclude root is TRUE. + */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + 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); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + 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); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 2; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq3, "find_seq3"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 1, find first is TRUE, exclude root is TRUE. Will + * retrieve only Provider_child. + */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, TRUE, TRUE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + 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); + + node2 = NULL; + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + + idx[0] = idx[1] = 0; + hr = SafeArrayGetElement(out_req, idx, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + + /* node2 is now set as Provider_child. */ + hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + IUnknown_AddRef((IUnknown *)node2); + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq4, "find_seq4"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * 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. + */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + 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); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 2; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq5, "find_seq5"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 0, find first is FALSE, exclude root is FALSE. + * Provider_child has 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 stop at Provider_child. + */ + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); + hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); + 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); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq6, "find_seq6"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + todo_wine ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + + initialize_provider_tree(FALSE); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 1, find first is FALSE, exclude root is FALSE. + * The cache request view condition is used to determine tree depth, if an + * element matches the cache request view condition, depth is incremented. + * Since Provider_child does not, Provider_child_child, Provider_child_child2, + * and Provider_child2 are all considered to be at depth 1. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond[0], UIA_IsContentElementPropertyId, &v, 0); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_TRUE; + set_property_condition(&prop_cond[1], UIA_IsControlElementPropertyId, &v, 0); + + set_provider_prop_override(&Provider, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 4; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq7, "find_seq7"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + initialize_provider_tree(FALSE); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Same test as before, except Provider has a runtime id. + */ + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond[0], UIA_IsContentElementPropertyId, &v, 0); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_TRUE; + set_property_condition(&prop_cond[1], UIA_IsControlElementPropertyId, &v, 0); + + set_provider_prop_override(&Provider, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 4; + exp_elems[1] = 1; + + idx[0] = 2; + idx[1] = 0; + hr = SafeArrayGetElement(out_req, idx, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + + /* node2 is now set as Provider_child_child2. */ + hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + IUnknown_AddRef((IUnknown *)node2); + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq8, "find_seq8"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + initialize_provider_tree(FALSE); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of 1, find first is FALSE, exclude root is FALSE. + * Starting at Provider_child_child2, find will be able to traverse the + * tree in the same order as it would if we had started at the tree root + * Provider, retrieving Provider_child2 as a sibling and + * Provider_child2_child as a node at depth 1. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond[0], UIA_IsContentElementPropertyId, &v, 0); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_TRUE; + set_property_condition(&prop_cond[1], UIA_IsControlElementPropertyId, &v, 0); + + prop_override.prop_id = UIA_IsContentElementPropertyId; + V_VT(&prop_override.val) = VT_BOOL; + V_BOOL(&prop_override.val) = VARIANT_FALSE; + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + set_provider_prop_override(&Provider_child2_child, &prop_override, 1); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); + hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 3; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq9, "find_seq9"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + initialize_provider_tree(FALSE); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + todo_wine ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + + /* + * Maximum find depth of 1, find first is FALSE, exclude root is TRUE. + * Exclude root applies to the first node that matches the view + * condition, and not the node that is passed into UiaFind(). Since + * Provider doesn't match our view condition here, Provider_child will be + * excluded. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond[0], UIA_IsContentElementPropertyId, &v, 0); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_TRUE; + set_property_condition(&prop_cond[1], UIA_IsControlElementPropertyId, &v, 0); + + set_provider_prop_override(&Provider_child, &prop_override, 1); + set_provider_prop_override(&Provider_child2, &prop_override, 1); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&prop_cond[1]); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + + ok_method_sequence(find_seq10, "find_seq10"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + initialize_provider_tree(FALSE); + for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) + init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL); + + /* + * Maximum find depth of -1, find first is TRUE, exclude root is FALSE. + * Provider_child_child2 is the only element in the tree to match our + * condition. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond[0], UIA_IsContentElementPropertyId, &v, 0); + set_property_override(&prop_override, UIA_IsContentElementPropertyId, &v); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_TRUE; + set_property_condition(&prop_cond[1], UIA_IsControlElementPropertyId, &v, 0); + + set_provider_prop_override(&Provider_child_child2, &prop_override, 1); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond[0], TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + set_find_params(&find_params, -1, TRUE, FALSE, (struct UiaCondition *)&prop_cond[1]); + hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); + 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); + + if (SUCCEEDED(hr)) + { + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 1; + + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; + } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq11, "find_seq11"); + } + + SafeArrayDestroy(out_req); + SafeArrayDestroy(offsets); + SafeArrayDestroy(tree_structs); + + initialize_provider_tree(TRUE); + + 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 @@ -8268,6 +9274,7 @@ START_TEST(uiautomation) launch_test_process(argv[0], "UiaRegisterProviderCallback"); test_UiaGetUpdatedCache(); test_UiaNavigate(); + test_UiaFind(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible"); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index dfacebc456f..1025b40446c 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2559,3 +2559,13 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct
return hr; } + +/*********************************************************************** + * UiaFind (uiautomationcore.@) + */ +HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, struct UiaCacheRequest *cache_req, + SAFEARRAY **out_req, SAFEARRAY **out_offsets, SAFEARRAY **out_tree_structs) +{ + FIXME("(%p, %p, %p, %p, %p, %p): stub\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs); + return E_NOTIMPL; +} diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec index c14b457fd47..0ad0cb6f437 100644 --- a/dlls/uiautomationcore/uiautomationcore.spec +++ b/dlls/uiautomationcore/uiautomationcore.spec @@ -59,7 +59,7 @@ @ stdcall UiaDisconnectProvider(ptr) @ stub UiaEventAddWindow @ stub UiaEventRemoveWindow -@ stub UiaFind +@ stdcall UiaFind(ptr ptr ptr ptr ptr ptr) @ stub UiaGetErrorDescription @ stub UiaGetPatternProvider @ stdcall UiaGetPropertyValue(ptr long ptr) diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h index 7025392cbbc..efb5eb35d06 100644 --- a/include/uiautomationcoreapi.h +++ b/include/uiautomationcoreapi.h @@ -385,6 +385,13 @@ struct UiaWindowClosedEventArgs { int cRuntimeIdLen; };
+struct UiaFindParams { + int MaxDepth; + BOOL FindFirst; + BOOL ExcludeRoot; + struct UiaCondition *pFindCondition; +}; + typedef SAFEARRAY * WINAPI UiaProviderCallback(HWND hwnd,enum ProviderType providerType);
HRESULT WINAPI UiaGetReservedMixedAttributeValue(IUnknown **value); @@ -409,6 +416,8 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct); HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct UiaCondition *nav_condition, struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct); +HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, + SAFEARRAY **out_offsets, SAFEARRAY **out_tree_structs);
#ifdef __cplusplus }
From: Connor McAdams cmcadams@codeweavers.com
Implement UiaFind for single node searches.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 29 +-- dlls/uiautomationcore/uia_client.c | 276 ++++++++++++++++++++- 2 files changed, 287 insertions(+), 18 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f4767731ec3..23d17130068 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -9176,26 +9176,23 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, -1, TRUE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - 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);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq11, "find_seq11"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq11, "find_seq11");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1025b40446c..8e6b45de135 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -23,6 +23,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+static const struct UiaCondition UiaFalseCondition = { ConditionType_False }; + static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) { LONG ubound; @@ -873,6 +875,186 @@ static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *o return S_OK; }
+static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition); +static BOOL uia_condition_matched(HRESULT hr); + +/* + * Assuming we have a tree that looks like this: + * +-------+ + * | | + * | 1 | + * | | + * +---+---+ + * | + * +-----------+-----------+ + * | | | + * +---+---+ +---+---+ +---+---+ + * | | | | | | + * | 2 +---| 3 +---+ 4 | + * | | | | | | + * +---+---+ +-------+ +-------+ + * | + * +---+---+ + * | | + * | 5 | + * | | + * +-------+ + * If we start navigation of the tree from node 1, our visit order for a + * depth first search would be 1 -> 2 -> 5 -> 3 -> 4. + * + * However, if we start from the middle of the sequence at node 5 without the + * prior context of navigating from nodes 1 and 2, we need to use the function + * below to reach node 3, so we can visit the nodes within the tree in the + * same order of 5 -> 3 -> 4. + */ +static HRESULT traverse_uia_node_tree_siblings(HUIANODE huianode, struct UiaCondition *ascending_stop_cond, + int dir, BOOL at_root_level, HUIANODE *out_node) +{ + struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); + HUIANODE node2 = NULL; + HRESULT hr; + + *out_node = NULL; + + IWineUiaNode_AddRef(&node->IWineUiaNode_iface); + while (1) + { + hr = navigate_uia_node(node, dir, &node2); + if (FAILED(hr) || node2 || !at_root_level) + goto exit; + + hr = navigate_uia_node(node, NavigateDirection_Parent, &node2); + if (FAILED(hr) || !node2) + goto exit; + + hr = uia_condition_check(node2, ascending_stop_cond); + if (FAILED(hr) || uia_condition_matched(hr)) + { + UiaNodeRelease(node2); + node2 = NULL; + goto exit; + } + + IWineUiaNode_Release(&node->IWineUiaNode_iface); + node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node2); + } + +exit: + IWineUiaNode_Release(&node->IWineUiaNode_iface); + *out_node = node2; + + return hr; +} + +/* + * This function is used to traverse the 'raw' tree of HUIANODEs using a + * depth-first search. As each node in the tree is visited, it is checked + * against two conditions. + * + * The first is the view condition, which is a filter for what is considered to + * be a valid node in the current tree 'view'. The view condition essentially + * creates a sort of 'virtual' tree. UI Automation provides three default tree + * views: + * -The 'raw' view has the view condition as ConditionType_True, so the tree + * is completely unfiltered. All nodes are valid. + * -The 'control' view contains only nodes that do not return VARIANT_FALSE + * for UIA_IsControlElementPropertyId. + * -The 'content' view contains only nodes that do not return VARIANT_FALSE + * for both UIA_IsControlElementPropertyId and UIA_IsContentElementPropertyId. + * + * If the currently visited node is considered to be valid within our view + * condition, it is then checked against the search condition to determine if + * it should be returned. + */ +static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond, + struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond, + struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, int max_depth, int *cur_depth, + HUIANODE *out_node) +{ + HUIANODE node = huianode; + HRESULT hr; + + while (1) + { + struct uia_node *node_data = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node); + BOOL incr_depth = FALSE; + HUIANODE node2 = NULL; + + hr = uia_condition_check(node, view_cond); + if (FAILED(hr)) + goto exit; + + if (uia_condition_matched(hr)) + { + /* + * If this is a valid node within our treeview, we need to increment + * our current depth within the tree. + */ + incr_depth = TRUE; + hr = uia_condition_check(node, search_cond); + if (FAILED(hr)) + goto exit; + + if (uia_condition_matched(hr)) + { + IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); + *out_node = node; + hr = S_FALSE; + goto exit; + } + } + + if (incr_depth) + (*cur_depth)++; + + /* If we haven't exceeded our maximum traversal depth, visit children of this node. */ + if (max_depth < 0 || (*cur_depth) <= max_depth) + { + if (traversal_opts & TreeTraversalOptions_LastToFirstOrder) + hr = navigate_uia_node(node_data, NavigateDirection_LastChild, &node2); + else + hr = navigate_uia_node(node_data, NavigateDirection_FirstChild, &node2); + + if (SUCCEEDED(hr) && node2) + hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond, + traversal_opts, FALSE, max_depth, cur_depth, out_node); + + if (FAILED(hr) || hr == S_FALSE) + goto exit; + } + + if (incr_depth) + (*cur_depth)--; + + /* + * Before attempting to visit any siblings of huianode, make sure + * huianode doesn't match our stop condition. + */ + hr = uia_condition_check(node, pre_sibling_nav_stop_cond); + if (FAILED(hr) || uia_condition_matched(hr)) + goto exit; + + /* Now, check for any siblings to visit. */ + if (traversal_opts & TreeTraversalOptions_LastToFirstOrder) + hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_PreviousSibling, + at_root_level, &node2); + else + hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_NextSibling, + at_root_level, &node2); + + if (FAILED(hr) || !node2) + break; + + UiaNodeRelease(node); + node = node2; + } + +exit: + UiaNodeRelease(node); + + return hr; +} + /* * IWineUiaProvider interface. */ @@ -2566,6 +2748,96 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, SAFEARRAY **out_offsets, SAFEARRAY **out_tree_structs) { - FIXME("(%p, %p, %p, %p, %p, %p): stub\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs); - return E_NOTIMPL; + struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId }; + struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); + SAFEARRAY *runtime_id, *req, *offsets, *tree_structs; + struct UiaCondition *sibling_stop_cond; + int cur_depth = 0; + BSTR tree_struct; + HUIANODE node2; + HRESULT hr; + LONG idx; + + TRACE("(%p, %p, %p, %p, %p, %p)\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs); + + if (!node || !find_params || !cache_req || !out_req || !out_offsets || !out_tree_structs) + return E_INVALIDARG; + + *out_tree_structs = *out_offsets = *out_req = tree_structs = offsets = req = NULL; + + if (!find_params->FindFirst) + { + FIXME("Finding more than one HUIANODE is currently unimplemented.\n"); + return E_NOTIMPL; + } + + if (find_params->ExcludeRoot) + { + FIXME("ExcludeRoot currently unimplemented.\n"); + return E_NOTIMPL; + } + + /* + * If the initial node has a runtime ID, we'll use it as a stop + * condition. + */ + hr = UiaGetRuntimeId(huianode, &runtime_id); + if (SUCCEEDED(hr) && runtime_id) + { + V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY; + V_ARRAY(&prop_cond.Value) = runtime_id; + sibling_stop_cond = (struct UiaCondition *)&prop_cond; + } + else + sibling_stop_cond = (struct UiaCondition *)&UiaFalseCondition; + + node2 = NULL; + IWineUiaNode_AddRef(&node->IWineUiaNode_iface); + hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond, + cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->MaxDepth, &cur_depth, &node2); + if (FAILED(hr) || !node2) + goto exit; + + if (!(offsets = SafeArrayCreateVector(VT_I4, 0, 1))) + { + hr = E_FAIL; + goto exit; + } + + if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, 1))) + { + hr = E_FAIL; + goto exit; + } + + hr = UiaGetUpdatedCache(node2, cache_req, NormalizeState_None, NULL, &req, &tree_struct); + UiaNodeRelease(node2); + if (FAILED(hr)) + goto exit; + + idx = 0; + hr = SafeArrayPutElement(tree_structs, &idx, tree_struct); + SysFreeString(tree_struct); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayPutElement(offsets, &idx, &idx); + if (FAILED(hr)) + goto exit; + + *out_tree_structs = tree_structs; + *out_offsets = offsets; + *out_req = req; + +exit: + VariantClear(&prop_cond.Value); + + if (FAILED(hr)) + { + SafeArrayDestroy(tree_structs); + SafeArrayDestroy(offsets); + SafeArrayDestroy(req); + } + + return hr; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 44 ++++++++++------------ dlls/uiautomationcore/uia_client.c | 43 ++++++++++++--------- 2 files changed, 45 insertions(+), 42 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 23d17130068..ef0e9b4253c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -8800,35 +8800,31 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, TRUE, TRUE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - 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);
- node2 = NULL; - if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1;
- idx[0] = idx[1] = 0; - hr = SafeArrayGetElement(out_req, idx, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + idx[0] = idx[1] = 0; + hr = SafeArrayGetElement(out_req, idx, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
- /* node2 is now set as Provider_child. */ - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - IUnknown_AddRef((IUnknown *)node2); + /* node2 is now set as Provider_child. */ + hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + IUnknown_AddRef((IUnknown *)node2);
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq4, "find_seq4"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq4, "find_seq4");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8910,7 +8906,7 @@ static void test_UiaFind(void) SafeArrayDestroy(offsets); SafeArrayDestroy(tree_structs);
- todo_wine ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
initialize_provider_tree(FALSE); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 8e6b45de135..451143c4446 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -968,8 +968,8 @@ exit: */ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond, struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond, - struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, int max_depth, int *cur_depth, - HUIANODE *out_node) + struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL *root_found, + int max_depth, int *cur_depth, HUIANODE *out_node) { HUIANODE node = huianode; HRESULT hr; @@ -991,17 +991,23 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi * our current depth within the tree. */ incr_depth = TRUE; - hr = uia_condition_check(node, search_cond); - if (FAILED(hr)) - goto exit;
- if (uia_condition_matched(hr)) + if (*root_found) { - IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); - *out_node = node; - hr = S_FALSE; - goto exit; + hr = uia_condition_check(node, search_cond); + if (FAILED(hr)) + goto exit; + + if (uia_condition_matched(hr)) + { + IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); + *out_node = node; + hr = S_FALSE; + goto exit; + } } + + *root_found = TRUE; }
if (incr_depth) @@ -1017,7 +1023,7 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (SUCCEEDED(hr) && node2) hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond, - traversal_opts, FALSE, max_depth, cur_depth, out_node); + traversal_opts, FALSE, root_found, max_depth, cur_depth, out_node);
if (FAILED(hr) || hr == S_FALSE) goto exit; @@ -2754,6 +2760,7 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str struct UiaCondition *sibling_stop_cond; int cur_depth = 0; BSTR tree_struct; + BOOL root_found; HUIANODE node2; HRESULT hr; LONG idx; @@ -2771,12 +2778,6 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str return E_NOTIMPL; }
- if (find_params->ExcludeRoot) - { - FIXME("ExcludeRoot currently unimplemented.\n"); - return E_NOTIMPL; - } - /* * If the initial node has a runtime ID, we'll use it as a stop * condition. @@ -2791,10 +2792,16 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str else sibling_stop_cond = (struct UiaCondition *)&UiaFalseCondition;
+ if (find_params->ExcludeRoot) + root_found = FALSE; + else + root_found = TRUE; + node2 = NULL; IWineUiaNode_AddRef(&node->IWineUiaNode_iface); hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond, - cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->MaxDepth, &cur_depth, &node2); + cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, &root_found, find_params->MaxDepth, + &cur_depth, &node2); if (FAILED(hr) || !node2) goto exit;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 85 ++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 10 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 451143c4446..8f2e7339cfc 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -25,6 +25,70 @@ WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
static const struct UiaCondition UiaFalseCondition = { ConditionType_False };
+static BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) +{ + SIZE_T max_capacity, new_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(1, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = count; + + if (!*elements) + new_elements = heap_alloc_zero(new_capacity * size); + else + new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); + if (!new_elements) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + return TRUE; +} + +struct uia_node_array { + HUIANODE *nodes; + int node_count; + SIZE_T node_arr_size; +}; + +static void clear_node_array(struct uia_node_array *nodes) +{ + if (nodes->nodes) + { + int i; + + for (i = 0; i < nodes->node_count; i++) + UiaNodeRelease(nodes->nodes[i]); + + heap_free(nodes->nodes); + } + + memset(nodes, 0, sizeof(*nodes)); +} + +static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE node) +{ + if (!uia_array_reserve((void **)&out_nodes->nodes, &out_nodes->node_arr_size, out_nodes->node_count + 1, + sizeof(node))) + return E_OUTOFMEMORY; + + IWineUiaNode_AddRef((IWineUiaNode *)node); + out_nodes->nodes[out_nodes->node_count] = node; + out_nodes->node_count++; + + return S_OK; +} + static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) { LONG ubound; @@ -969,7 +1033,7 @@ exit: static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond, struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond, struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL *root_found, - int max_depth, int *cur_depth, HUIANODE *out_node) + int max_depth, int *cur_depth, struct uia_node_array *out_nodes) { HUIANODE node = huianode; HRESULT hr; @@ -1000,8 +1064,10 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (uia_condition_matched(hr)) { - IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); - *out_node = node; + hr = add_node_to_node_array(out_nodes, node); + if (FAILED(hr)) + goto exit; + hr = S_FALSE; goto exit; } @@ -1023,7 +1089,7 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (SUCCEEDED(hr) && node2) hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond, - traversal_opts, FALSE, root_found, max_depth, cur_depth, out_node); + traversal_opts, FALSE, root_found, max_depth, cur_depth, out_nodes);
if (FAILED(hr) || hr == S_FALSE) goto exit; @@ -2758,10 +2824,10 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); SAFEARRAY *runtime_id, *req, *offsets, *tree_structs; struct UiaCondition *sibling_stop_cond; + struct uia_node_array nodes = { 0 }; int cur_depth = 0; BSTR tree_struct; BOOL root_found; - HUIANODE node2; HRESULT hr; LONG idx;
@@ -2797,12 +2863,11 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str else root_found = TRUE;
- node2 = NULL; IWineUiaNode_AddRef(&node->IWineUiaNode_iface); hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond, cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, &root_found, find_params->MaxDepth, - &cur_depth, &node2); - if (FAILED(hr) || !node2) + &cur_depth, &nodes); + if (FAILED(hr) || !nodes.node_count) goto exit;
if (!(offsets = SafeArrayCreateVector(VT_I4, 0, 1))) @@ -2817,8 +2882,7 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str goto exit; }
- hr = UiaGetUpdatedCache(node2, cache_req, NormalizeState_None, NULL, &req, &tree_struct); - UiaNodeRelease(node2); + hr = UiaGetUpdatedCache(nodes.nodes[0], cache_req, NormalizeState_None, NULL, &req, &tree_struct); if (FAILED(hr)) goto exit;
@@ -2838,6 +2902,7 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str
exit: VariantClear(&prop_cond.Value); + clear_node_array(&nodes);
if (FAILED(hr)) {
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 339 ++++++++++----------- dlls/uiautomationcore/uia_client.c | 180 ++++++++--- 2 files changed, 297 insertions(+), 222 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ef0e9b4253c..f12975b60dd 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -8677,37 +8677,34 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, -1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - 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); - todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref); - todo_wine ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref); + ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); - add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child", TRUE); - add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child_child2", TRUE); - add_provider_desc(&exp_node_desc[4], L"Main", L"Provider_child2", TRUE); - add_provider_desc(&exp_node_desc[5], L"Main", L"Provider_child2_child", TRUE); - add_provider_desc(&exp_node_desc[6], L"Main", L"Provider_child2_child_child", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 7; - exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[4], L"Main", L"Provider_child2", TRUE); + add_provider_desc(&exp_node_desc[5], L"Main", L"Provider_child2_child", TRUE); + add_provider_desc(&exp_node_desc[6], L"Main", L"Provider_child2_child_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 7; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq1, "find_seq1"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq1, "find_seq1");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8723,31 +8720,28 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - 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); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - - if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); - add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 3; - exp_elems[1] = 1; + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 3; + exp_elems[1] = 1;
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq2, "find_seq2"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; }
+ test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq2, "find_seq2"); + SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); SafeArrayDestroy(tree_structs); @@ -8762,28 +8756,25 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - 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); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- if (SUCCEEDED(hr)) - { - 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); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 2; - exp_elems[1] = 1; + 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); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 2; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq3, "find_seq3"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq3, "find_seq3");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8843,27 +8834,24 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- if (SUCCEEDED(hr)) - { - 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); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 2; - exp_elems[1] = 1; + 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); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 2; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq5, "find_seq5"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq5, "find_seq5");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8883,24 +8871,21 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE); + 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); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq6, "find_seq6"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq6, "find_seq6");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8938,31 +8923,28 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); - add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); - add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 4; - exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 4; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq7, "find_seq7"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq7, "find_seq7");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -8994,41 +8976,38 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); - todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref); + ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); - add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); - add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 4; - exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[3], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 4; + exp_elems[1] = 1;
- idx[0] = 2; - idx[1] = 0; - hr = SafeArrayGetElement(out_req, idx, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + idx[0] = 2; + idx[1] = 0; + hr = SafeArrayGetElement(out_req, idx, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
- /* node2 is now set as Provider_child_child2. */ - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - IUnknown_AddRef((IUnknown *)node2); + /* node2 is now set as Provider_child_child2. */ + hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + IUnknown_AddRef((IUnknown *)node2);
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq8, "find_seq8"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq8, "find_seq8");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -9064,29 +9043,26 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref);
- if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); - add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); - add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2_child", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 3; - exp_elems[1] = 1; + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE); + add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE); + add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 3; + exp_elems[1] = 1;
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); - ok_method_sequence(find_seq9, "find_seq9"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + ok_method_sequence(find_seq9, "find_seq9");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); @@ -9096,7 +9072,7 @@ static void test_UiaFind(void) for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++) init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL);
- todo_wine ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); ok(Provider_child_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
/* @@ -9122,27 +9098,24 @@ static void test_UiaFind(void) AutomationElementMode_Full); set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&prop_cond[1]); hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - - if (SUCCEEDED(hr)) - { - add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 1; + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
- test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 1;
- for (i = 0; i < exp_elems[0]; i++) - { - exp_offset[i] = i; - exp_tree_struct[i] = L"P)"; - } - test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc);
- ok_method_sequence(find_seq10, "find_seq10"); + for (i = 0; i < exp_elems[0]; i++) + { + exp_offset[i] = i; + exp_tree_struct[i] = L"P)"; } + test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset); + + ok_method_sequence(find_seq10, "find_seq10");
SafeArrayDestroy(out_req); SafeArrayDestroy(offsets); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 8f2e7339cfc..d6ea0a06b8e 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -89,25 +89,17 @@ static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE return S_OK; }
-static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) +static HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) { LONG ubound; HRESULT hr; - UINT dims;
*lbound = *elems = 0; - dims = SafeArrayGetDim(sa); - if (dims != 1) - { - WARN("Invalid dimensions %d for safearray.\n", dims); - return E_FAIL; - } - - hr = SafeArrayGetLBound(sa, 1, lbound); + hr = SafeArrayGetLBound(sa, dim, lbound); if (FAILED(hr)) return hr;
- hr = SafeArrayGetUBound(sa, 1, &ubound); + hr = SafeArrayGetUBound(sa, dim, &ubound); if (FAILED(hr)) return hr;
@@ -115,6 +107,21 @@ static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) return S_OK; }
+static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) +{ + UINT dims; + + *lbound = *elems = 0; + dims = SafeArrayGetDim(sa); + if (dims != 1) + { + WARN("Invalid dimensions %d for safearray.\n", dims); + return E_FAIL; + } + + return get_safearray_dim_bounds(sa, 1, lbound, elems); +} + int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) { LONG i, idx, lbound[2], elems[2]; @@ -1032,8 +1039,8 @@ exit: */ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond, struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond, - struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL *root_found, - int max_depth, int *cur_depth, struct uia_node_array *out_nodes) + struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL find_first, + BOOL *root_found, int max_depth, int *cur_depth, struct uia_node_array *out_nodes) { HUIANODE node = huianode; HRESULT hr; @@ -1068,8 +1075,11 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi if (FAILED(hr)) goto exit;
- hr = S_FALSE; - goto exit; + if (find_first) + { + hr = S_FALSE; + goto exit; + } } }
@@ -1089,7 +1099,7 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (SUCCEEDED(hr) && node2) hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond, - traversal_opts, FALSE, root_found, max_depth, cur_depth, out_nodes); + traversal_opts, FALSE, find_first, root_found, max_depth, cur_depth, out_nodes);
if (FAILED(hr) || hr == S_FALSE) goto exit; @@ -2814,6 +2824,50 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct return hr; }
+/* Combine multiple cache requests into a single SAFEARRAY. */ +static HRESULT uia_cache_request_combine(SAFEARRAY **reqs, int reqs_count, SAFEARRAY *out_req) +{ + LONG idx[2], lbound[2], elems[2], cur_offset; + int i, x, y; + HRESULT hr; + VARIANT v; + + for (i = cur_offset = 0; i < reqs_count; i++) + { + if (!reqs[i]) + continue; + + for (x = 0; x < 2; x++) + { + hr = get_safearray_dim_bounds(reqs[i], 1 + x, &lbound[x], &elems[x]); + if (FAILED(hr)) + return hr; + } + + for (x = 0; x < elems[0]; x++) + { + for (y = 0; y < elems[1]; y++) + { + idx[0] = x + lbound[0]; + idx[1] = y + lbound[1]; + hr = SafeArrayGetElement(reqs[i], idx, &v); + if (FAILED(hr)) + return hr; + + idx[0] = x + cur_offset; + idx[1] = y; + hr = SafeArrayPutElement(out_req, idx, &v); + if (FAILED(hr)) + return hr; + } + } + + cur_offset += elems[0]; + } + + return S_OK; +} + /*********************************************************************** * UiaFind (uiautomationcore.@) */ @@ -2822,14 +2876,15 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str { struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId }; struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); - SAFEARRAY *runtime_id, *req, *offsets, *tree_structs; + SAFEARRAY *runtime_id, *req, *offsets, *tree_structs, **tmp_reqs; struct UiaCondition *sibling_stop_cond; struct uia_node_array nodes = { 0 }; - int cur_depth = 0; + LONG idx, lbound, elems, cur_offset; + SAFEARRAYBOUND sabound[2]; + int i, cur_depth = 0; BSTR tree_struct; BOOL root_found; HRESULT hr; - LONG idx;
TRACE("(%p, %p, %p, %p, %p, %p)\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs);
@@ -2837,12 +2892,7 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str return E_INVALIDARG;
*out_tree_structs = *out_offsets = *out_req = tree_structs = offsets = req = NULL; - - if (!find_params->FindFirst) - { - FIXME("Finding more than one HUIANODE is currently unimplemented.\n"); - return E_NOTIMPL; - } + tmp_reqs = NULL;
/* * If the initial node has a runtime ID, we'll use it as a stop @@ -2865,36 +2915,81 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str
IWineUiaNode_AddRef(&node->IWineUiaNode_iface); hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond, - cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, &root_found, find_params->MaxDepth, - &cur_depth, &nodes); + cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->FindFirst, &root_found, + find_params->MaxDepth, &cur_depth, &nodes); if (FAILED(hr) || !nodes.node_count) goto exit;
- if (!(offsets = SafeArrayCreateVector(VT_I4, 0, 1))) + if (!(offsets = SafeArrayCreateVector(VT_I4, 0, nodes.node_count))) { hr = E_FAIL; goto exit; }
- if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, 1))) + if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, nodes.node_count))) { hr = E_FAIL; goto exit; }
- hr = UiaGetUpdatedCache(nodes.nodes[0], cache_req, NormalizeState_None, NULL, &req, &tree_struct); - if (FAILED(hr)) + if (!(tmp_reqs = heap_alloc_zero(sizeof(*tmp_reqs) * nodes.node_count))) + { + hr = E_OUTOFMEMORY; goto exit; + }
- idx = 0; - hr = SafeArrayPutElement(tree_structs, &idx, tree_struct); - SysFreeString(tree_struct); - if (FAILED(hr)) - goto exit; + /* + * Get a count of how many total nodes we'll need to return, as well as + * set the tree structure strings and cache request offsets for our final + * combined SAFEARRAY. + */ + for (i = cur_offset = 0; i < nodes.node_count; i++) + { + hr = UiaGetUpdatedCache(nodes.nodes[i], cache_req, NormalizeState_None, NULL, &tmp_reqs[i], &tree_struct); + if (FAILED(hr)) + goto exit;
- hr = SafeArrayPutElement(offsets, &idx, &idx); - if (FAILED(hr)) - goto exit; + idx = i; + hr = SafeArrayPutElement(tree_structs, &idx, tree_struct); + SysFreeString(tree_struct); + if (FAILED(hr)) + goto exit; + + hr = SafeArrayPutElement(offsets, &idx, &cur_offset); + if (FAILED(hr)) + goto exit; + + if (!tmp_reqs[i]) + continue; + + hr = get_safearray_dim_bounds(tmp_reqs[i], 1, &lbound, &elems); + if (FAILED(hr)) + goto exit; + + cur_offset += elems; + } + + if (nodes.node_count == 1) + { + req = tmp_reqs[0]; + heap_free(tmp_reqs); + tmp_reqs = NULL; + } + else + { + sabound[0].lLbound = sabound[1].lLbound = 0; + sabound[0].cElements = cur_offset; + sabound[1].cElements = 1 + cache_req->cProperties + cache_req->cPatterns; + if (!(req = SafeArrayCreate(VT_VARIANT, 2, sabound))) + { + hr = E_FAIL; + goto exit; + } + + hr = uia_cache_request_combine(tmp_reqs, nodes.node_count, req); + if (FAILED(hr)) + goto exit; + }
*out_tree_structs = tree_structs; *out_offsets = offsets; @@ -2904,6 +2999,13 @@ exit: VariantClear(&prop_cond.Value); clear_node_array(&nodes);
+ if (tmp_reqs) + { + for (i = 0; i < nodes.node_count; i++) + SafeArrayDestroy(tmp_reqs[i]); + heap_free(tmp_reqs); + } + if (FAILED(hr)) { SafeArrayDestroy(tree_structs);
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 tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126082
Your paranoid android.
=== w10pro64_zh_CN (testbot log) ===
WineRunTask.pl:error: The previous 1 run(s) terminated abnormally
On Fri Nov 11 22:10:49 2022 +0000, Connor McAdams wrote:
changed this line in [version 4 of the diff](/wine/wine/-/merge_requests/1249/diffs?diff_id=18274&start_sha=e73553a645cb1dba51e44be53c197d2a7525af9b#58d90e31c9d28aee1bd4c53093a1f039ee491f9d_45_46)
I've replaced the function body with the one from wined3d now instead.
V4: - Use a single boolean variable for ExcludeRoot. - Add/use new cleaner child assignment function in tests. - Don't recursively call `traverse_uia_node_tree()` when navigating to siblings. - Replace the `uia_array_reserve()` function body with the one from wined3d.