From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 106 +++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 72 +++++++++++--- 2 files changed, 166 insertions(+), 12 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 25b975fa280..193f1470029 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1162,6 +1162,8 @@ static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_prox static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); static void set_provider_prop_override(struct Provider *prov, struct Provider_prop_override *override, int count); static void set_property_override(struct Provider_prop_override *override, int prop_id, VARIANT *val); +static void initialize_provider_tree(BOOL initialize_nav_links); +static void provider_add_child(struct Provider *prov, struct Provider *child);
static const WCHAR *uia_bstr_prop_str = L"uia-string"; static const ULONG uia_i4_prop_val = 0xdeadbeef; @@ -8659,6 +8661,40 @@ static const struct prov_method_sequence nav_seq14[] = { { 0 } };
+static const struct prov_method_sequence nav_seq15[] = { + NODE_CREATE_SEQ(&Provider_child2_child_child), + { &Provider_child2_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ +}; + +static const struct prov_method_sequence nav_seq16[] = { + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence nav_seq17[] = { + { &Provider_child2_child_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2_child), + { &Provider_child2_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child2), + { &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + static void set_provider_nav_ifaces(struct Provider *prov, struct Provider *parent, struct Provider *frag_root, struct Provider *prev_sibling, struct Provider *next_sibling, struct Provider *first_child, struct Provider *last_child) @@ -8686,8 +8722,10 @@ static void set_provider_nav_ifaces(struct Provider *prov, struct Provider *pare
static void test_UiaNavigate(void) { + struct Provider_prop_override prop_override; LONG exp_lbound[2], exp_elems[2], idx[2], i; struct node_provider_desc exp_node_desc[4]; + struct UiaPropertyCondition prop_cond; struct UiaCacheRequest cache_req; HUIANODE node, node2, node3; SAFEARRAY *out_req; @@ -9195,6 +9233,74 @@ static void test_UiaNavigate(void) prov_root = NULL; UiaRegisterProviderCallback(NULL);
+ /* + * Conditional navigation for conditions other than ConditionType_True. + */ + initialize_provider_tree(TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + provider_add_child(&Provider, &Provider_child2); + provider_add_child(&Provider_child2, &Provider_child2_child); + provider_add_child(&Provider_child2_child, &Provider_child2_child_child); + + hr = UiaNodeFromProvider(&Provider_child2_child_child.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child2_child_child", TRUE); + VariantClear(&v); + + ok_method_sequence(nav_seq15, "nav_seq15"); + + /* + * Navigate from Provider_child2_child_child to a parent that has + * UIA_IsControlElementPropertyId set to FALSE. + */ + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + tree_struct = NULL; + out_req = NULL; + hr = UiaNavigate(node, NavigateDirection_Parent, (struct UiaCondition *)&prop_cond, &cache_req, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!out_req, "out_req != NULL\n"); + ok(!tree_struct, "tree_struct != NULL\n"); + ok_method_sequence(nav_seq16, "nav_seq16"); + + /* Provider will now return FALSE for UIA_IsControlElementPropertyId. */ + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + set_cache_request(&cache_req, NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + tree_struct = NULL; + out_req = NULL; + hr = UiaNavigate(node, NavigateDirection_Parent, (struct UiaCondition *)&prop_cond, &cache_req, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + + ok(!wcscmp(tree_struct, L"P)"), "tree structure %s\n", debugstr_w(tree_struct)); + ok_method_sequence(nav_seq17, "nav_seq17"); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child2_child_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref); + CoUninitialize(); DestroyWindow(hwnd); UnregisterClassA("UiaNavigate class", NULL); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 4ec12423533..9b3d734defd 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -934,6 +934,63 @@ static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *o
static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition); static BOOL uia_condition_matched(HRESULT hr); +static HRESULT conditional_navigate_uia_node(struct uia_node *node, int nav_dir, struct UiaCondition *cond, + HUIANODE *out_node) +{ + HRESULT hr; + + *out_node = NULL; + switch (nav_dir) + { + case NavigateDirection_Parent: + { + struct uia_node *node_data = node; + HUIANODE parent; + + IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); + while (1) + { + hr = navigate_uia_node(node_data, NavigateDirection_Parent, &parent); + if (FAILED(hr) || !parent) + break; + + hr = uia_condition_check(parent, cond); + if (FAILED(hr)) + break; + + if (uia_condition_matched(hr)) + { + *out_node = parent; + break; + } + + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + node_data = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)parent); + } + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + break; + } + + case NavigateDirection_NextSibling: + case NavigateDirection_PreviousSibling: + case NavigateDirection_FirstChild: + case NavigateDirection_LastChild: + if (cond->ConditionType != ConditionType_True) + { + FIXME("ConditionType %d based navigation for dir %d is not implemented.\n", cond->ConditionType, nav_dir); + return E_NOTIMPL; + } + + hr = navigate_uia_node(node, nav_dir, out_node); + break; + + default: + WARN("Invalid NavigateDirection %d\n", nav_dir); + return E_INVALIDARG; + } + + return hr; +}
/* * Assuming we have a tree that looks like this: @@ -3068,7 +3125,7 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct) { struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode); - HUIANODE node2; + HUIANODE node2 = NULL; HRESULT hr;
TRACE("(%p, %u, %p, %p, %p, %p)\n", huianode, dir, nav_condition, cache_req, out_req, @@ -3080,17 +3137,8 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct *out_req = NULL; *tree_struct = NULL;
- if (nav_condition->ConditionType != ConditionType_True) - { - FIXME("ConditionType %d based navigation is not implemented.\n", nav_condition->ConditionType); - return E_NOTIMPL; - } - - hr = navigate_uia_node(node, dir, &node2); - if (FAILED(hr)) - return hr; - - if (node2) + hr = conditional_navigate_uia_node(node, dir, nav_condition, &node2); + if (SUCCEEDED(hr) && node2) { hr = UiaGetUpdatedCache(node2, cache_req, NormalizeState_None, NULL, out_req, tree_struct); if (FAILED(hr))