1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00

uiautomationcore: Implement conditional NavigateDirection_Parent navigation.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2023-03-23 15:56:45 -04:00 committed by Alexandre Julliard
parent b532f695c1
commit f93bd3ff2c
2 changed files with 166 additions and 12 deletions

View file

@ -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);

View file

@ -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))