[PATCH 0/6] MR2458: uiautomationcore: Test fixes, cache normalization, and root element retrieval functions.
From: Connor McAdams <cmcadams(a)codeweavers.com> Windows 10 1909 doesn't use IAccessible2 interfaces while comparing IAccessible interfaces, so skip the interface comparison tests. Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53269 --- dlls/uiautomationcore/tests/uiautomation.c | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f1d57336d37..6aa5e67c440 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -2739,9 +2739,9 @@ static void test_uia_prov_from_acc_ia2(void) HRESULT hr; /* Only one exposes an IA2 interface, no match. */ - set_accessible_props(&Accessible, 0, 0, 0, L"acc_name", 0, 0, 0, 0); + set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, 0, 0, L"acc_name", 0, 0, 0, 0); set_accessible_ia2_props(&Accessible, TRUE, 0); - set_accessible_props(&Accessible2, 0, 0, 0, L"acc_name", 0, 0, 0, 0); + set_accessible_props(&Accessible2, ROLE_SYSTEM_TEXT, 0, 0, L"acc_name", 0, 0, 0, 0); set_accessible_ia2_props(&Accessible2, FALSE, 0); hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); @@ -2755,6 +2755,11 @@ static void test_uia_prov_from_acc_ia2(void) acc_client = &Accessible2.IAccessible_iface; SET_EXPECT(winproc_GETOBJECT_CLIENT); + /* The four below are only called on Win10v1909. */ + SET_EXPECT(Accessible_get_accRole); + SET_EXPECT(Accessible2_get_accRole); + SET_EXPECT(Accessible2_QI_IAccIdentity); + SET_EXPECT(Accessible2_get_accParent); elprov2 = (void *)0xdeadbeef; hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2762,6 +2767,27 @@ static void test_uia_prov_from_acc_ia2(void) ok(Accessible2.ref == 1, "Unexpected refcnt %ld\n", Accessible2.ref); CHECK_CALLED(winproc_GETOBJECT_CLIENT); + /* + * Win10v1909 has IAccessible2 support, but it's not used for checking if + * two IAccessible interfaces match. Skip the comparison tests for this + * Windows version. + */ + if (called_Accessible_get_accRole) + { + IRawElementProviderSimple_Release(elprov); + CHECK_CALLED(Accessible_get_accRole); + CHECK_CALLED(Accessible2_get_accRole); + CHECK_CALLED(Accessible2_QI_IAccIdentity); + CHECK_CALLED(Accessible2_get_accParent); + win_skip("Win10v1909 doesn't support IAccessible2 interface comparsion, skipping tests.\n"); + return; + } + called_Accessible_get_accRole = expect_Accessible_get_accRole = 0; + called_Accessible2_get_accRole = expect_Accessible2_get_accRole = 0; + called_Accessible2_QI_IAccIdentity = expect_Accessible2_QI_IAccIdentity = 0; + called_Accessible2_get_accParent = expect_Accessible2_get_accParent = 0; + + Accessible.role = Accessible2.role = 0; elprov2 = (void *)0xdeadbeef; acc_client = NULL; hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> --- dlls/uiautomationcore/tests/uiautomation.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6aa5e67c440..b957999a365 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -11841,6 +11841,15 @@ static void test_CUIAutomation_TreeWalker_ifaces(IUIAutomation *uia_iface) provider_add_child(&Provider, &Provider_child); provider_add_child(&Provider, &Provider_child2); + /* NULL input argument tests. */ + hr = IUIAutomationTreeWalker_GetFirstChildElement(walker, element, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + element2 = (void *)0xdeadbeef; + hr = IUIAutomationTreeWalker_GetFirstChildElement(walker, NULL, &element2); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!element2, "element2 != NULL\n"); + /* NavigateDirection_FirstChild. */ element2 = NULL; hr = IUIAutomationTreeWalker_GetFirstChildElement(walker, element, &element2); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> --- dlls/uiautomationcore/tests/uiautomation.c | 85 +++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 10 +++ dlls/uiautomationcore/uiautomationcore.spec | 2 +- include/uiautomationcoreapi.h | 1 + 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b957999a365..ed02f35c665 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -9961,6 +9961,90 @@ static void test_UiaFind(void) CoUninitialize(); } +static const struct prov_method_sequence get_root_node_seq[] = { + /* These two are only done on Win10v1809+. */ + { &Provider_hwnd, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, + { &Provider_hwnd, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_proxy, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ + { &Provider_proxy, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_proxy, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_nc, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_hwnd, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static void test_UiaGetRootNode(void) +{ + HUIANODE node = NULL; + HRESULT hr; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* NULL input argument. */ + hr = UiaGetRootNode(NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* + * UiaGetRootNode is the same as calling UiaNodeFromHandle with the + * desktop window handle. + */ + initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, + GetDesktopWindow(), TRUE); + initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); + Provider_proxy.ignore_hwnd_prop = TRUE; + + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = UiaGetRootNode(&node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + ok(Provider_proxy.ref == 2, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 2, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + 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(), GetDesktopWindow()); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_proxy", TRUE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider_hwnd", FALSE); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", L"Provider_nc", FALSE); + VariantClear(&v); + } + + ok_method_sequence(get_root_node_seq, "get_root_node_seq"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider_proxy.ref == 1, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 1, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 1, "Unexpected refcnt %ld\n", Provider_nc.ref); + + initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, NULL, TRUE); + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, NULL, + TRUE); + initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, NULL, TRUE); + base_hwnd_prov = proxy_prov = nc_prov = NULL; + + UiaRegisterProviderCallback(NULL); + CoUninitialize(); +} + static HWND create_test_hwnd(const char *class_name) { WNDCLASSA cls = { 0 }; @@ -12138,6 +12222,7 @@ START_TEST(uiautomation) test_UiaNavigate(); test_UiaFind(); test_CUIAutomation(); + test_UiaGetRootNode(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible"); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 195974c25f6..2088932fed0 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2215,6 +2215,16 @@ HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode) return S_OK; } +/*********************************************************************** + * UiaGetRootNode (uiautomationcore.@) + */ +HRESULT WINAPI UiaGetRootNode(HUIANODE *huianode) +{ + TRACE("(%p)\n", huianode); + + return UiaNodeFromHandle(GetDesktopWindow(), huianode); +} + /*********************************************************************** * UiaNodeRelease (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec index 7168135487b..34856be6b36 100644 --- a/dlls/uiautomationcore/uiautomationcore.spec +++ b/dlls/uiautomationcore/uiautomationcore.spec @@ -65,7 +65,7 @@ @ stdcall UiaGetPropertyValue(ptr long ptr) @ stdcall UiaGetReservedMixedAttributeValue(ptr) @ stdcall UiaGetReservedNotSupportedValue(ptr) -@ stub UiaGetRootNode +@ stdcall UiaGetRootNode(ptr) @ stdcall UiaGetRuntimeId(ptr ptr) @ stdcall UiaGetUpdatedCache(ptr ptr long ptr ptr ptr) @ stub UiaHPatternObjectFromVariant diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h index 1d78113e1fd..320bb814b9c 100644 --- a/include/uiautomationcoreapi.h +++ b/include/uiautomationcoreapi.h @@ -544,6 +544,7 @@ BOOL WINAPI UiaNodeRelease(HUIANODE huianode); HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id); HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode); HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode); +HRESULT WINAPI UiaGetRootNode(HUIANODE *huianode); HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov); HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state, struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> --- dlls/uiautomationcore/tests/uiautomation.c | 69 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 17 +++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ed02f35c665..f0113f2a254 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12044,6 +12044,74 @@ static void test_CUIAutomation_TreeWalker_ifaces(IUIAutomation *uia_iface) UnregisterClassA("test_CUIAutomation_TreeWalker_ifaces class", NULL); } +static void test_GetRootElement(IUIAutomation *uia_iface) +{ + IUIAutomationElement *element; + HRESULT hr; + VARIANT v; + + hr = IUIAutomation_GetRootElement(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* NULL input argument. */ + hr = IUIAutomation_GetRootElement(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* + * UiaGetRootNode is the same as calling UiaNodeFromHandle with the + * desktop window handle. + */ + initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, + GetDesktopWindow(), TRUE); + initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); + Provider_proxy.ignore_hwnd_prop = TRUE; + + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = IUIAutomation_GetRootElement(uia_iface, &element); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element, "Node == NULL.\n"); + ok(Provider_proxy.ref == 2, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 2, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ProviderDescriptionPropertyId, TRUE, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), GetDesktopWindow()); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_proxy", TRUE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider_hwnd", FALSE); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", L"Provider_nc", FALSE); + VariantClear(&v); + } + + ok_method_sequence(get_root_node_seq, "get_root_node_seq"); + IUIAutomationElement_Release(element); + ok(Provider_proxy.ref == 1, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 1, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 1, "Unexpected refcnt %ld\n", Provider_nc.ref); + + initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, NULL, TRUE); + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, NULL, + TRUE); + initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, NULL, TRUE); + base_hwnd_prov = proxy_prov = nc_prov = NULL; + + UiaRegisterProviderCallback(NULL); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -12150,6 +12218,7 @@ static void test_CUIAutomation(void) test_Element_GetPropertyValue(uia_iface); test_Element_cache_methods(uia_iface); test_Element_Find(uia_iface); + test_GetRootElement(uia_iface); IUIAutomation_Release(uia_iface); CoUninitialize(); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index f1c28e81aa8..6cba9fd46db 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -2776,8 +2776,21 @@ static HRESULT WINAPI uia_iface_CompareRuntimeIds(IUIAutomation6 *iface, SAFEARR static HRESULT WINAPI uia_iface_GetRootElement(IUIAutomation6 *iface, IUIAutomationElement **root) { - FIXME("%p, %p: stub\n", iface, root); - return E_NOTIMPL; + struct uia_iface *uia_iface = impl_from_IUIAutomation6(iface); + HUIANODE node; + HRESULT hr; + + TRACE("%p, %p\n", iface, root); + + if (!root) + return E_POINTER; + + *root = NULL; + hr = UiaGetRootNode(&node); + if (FAILED(hr)) + return hr; + + return create_uia_element(root, uia_iface->is_cui8, node); } static HRESULT WINAPI uia_iface_ElementFromHandle(IUIAutomation6 *iface, UIA_HWND hwnd, IUIAutomationElement **out_elem) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
From: Connor McAdams <cmcadams(a)codeweavers.com> Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> --- dlls/uiautomationcore/tests/uiautomation.c | 109 +++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 54 ++++++++-- 2 files changed, 156 insertions(+), 7 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f0113f2a254..36e982af5ff 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1148,6 +1148,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; @@ -8412,6 +8414,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, METHOD_TODO }, /* 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, METHOD_TODO }, /* 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) @@ -8439,8 +8475,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; @@ -8954,6 +8992,77 @@ 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); + 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_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 2088932fed0..4ace3bc05ed 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2886,7 +2886,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, @@ -2898,15 +2898,55 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct *out_req = NULL; *tree_struct = NULL; - if (nav_condition->ConditionType != ConditionType_True) + switch (dir) { - FIXME("ConditionType %d based navigation is not implemented.\n", nav_condition->ConditionType); - return E_NOTIMPL; + 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, nav_condition); + if (FAILED(hr)) + break; + + if (uia_condition_matched(hr)) + { + node2 = parent; + break; + } + + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + node_data = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)parent); + } + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + break; } - hr = navigate_uia_node(node, dir, &node2); - if (FAILED(hr)) - return hr; + case NavigateDirection_NextSibling: + case NavigateDirection_PreviousSibling: + case NavigateDirection_FirstChild: + case NavigateDirection_LastChild: + if (nav_condition->ConditionType != ConditionType_True) + { + FIXME("ConditionType %d based navigation for dir %d is not implemented.\n", nav_condition->ConditionType, dir); + return E_NOTIMPL; + } + + hr = navigate_uia_node(node, dir, &node2); + if (FAILED(hr)) + return hr; + break; + + default: + return E_INVALIDARG; + } if (node2) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
From: Connor McAdams <cmcadams(a)codeweavers.com> If a normalize state other than NormalizeState_None is passed into UiaGetUpdatedCache and the passed in node doesn't match the normalization condition, attempt to get the first ancestor that does. Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com> --- dlls/uiautomationcore/tests/uiautomation.c | 85 ++++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 13 +++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 36e982af5ff..bddfaaeaea1 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -7623,6 +7623,34 @@ static const struct prov_method_sequence cache_req_seq7[] = { { 0 } }; +static const struct prov_method_sequence cache_req_seq8[] = { + NODE_CREATE_SEQ(&Provider_child), + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq9[] = { + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Done twice on Windows, but we shouldn't need to replicate this. */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Only done on Win10v1507 and below. */ + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence cache_req_seq10[] = { + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + /* Done twice on Windows, but we shouldn't need to replicate this. */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider), + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId. */ + { 0 } +}; + static const struct UiaCondition UiaTrueCondition = { ConditionType_True }; static const struct UiaCondition UiaFalseCondition = { ConditionType_False }; static void test_UiaGetUpdatedCache(void) @@ -8172,7 +8200,64 @@ static void test_UiaGetUpdatedCache(void) ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + + /* Normalization navigation tests. */ + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); + + hr = UiaNodeFromProvider(&Provider_child.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok_method_sequence(cache_req_seq8, "cache_req_seq8"); + + /* + * Neither Provider_child or Provider match this condition, return + * nothing. + */ + variant_init_bool(&v, FALSE); + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + SysFreeString(tree_struct); + ok_method_sequence(cache_req_seq9, "cache_req_seq9"); + + /* + * Provider now matches our condition, we'll get Provider in the cache + * request. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + init_node_provider_desc(&exp_node_desc[0], GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE); + + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full); + tree_struct = NULL; out_req = NULL; + hr = UiaGetUpdatedCache(node, &cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + test_cache_req_sa(out_req, exp_lbound, exp_elems, exp_node_desc); + ok(!wcscmp(tree_struct, L"P)"), "tree structure %s\n", debugstr_w(tree_struct)); + ok_method_sequence(cache_req_seq10, "cache_req_seq10"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + VariantClear(&v); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); IUnknown_Release(unk_ns); CoUninitialize(); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 4ace3bc05ed..a3d26ece0bd 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2821,8 +2821,17 @@ HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cac if (!uia_condition_matched(hr)) { - *tree_struct = SysAllocString(L""); - return S_OK; + /* Attempt to get the nearest ancestor matching this condition. */ + hr = UiaNavigate(huianode, NavigateDirection_Parent, cond, cache_req, out_req, tree_struct); + + /* + * UiaNavigate doesn't return an empty string if no node is found, + * but UiaGetUpdatedCacheRequest needs to. + */ + if (SUCCEEDED(hr) && !(*tree_struct)) + *tree_struct = SysAllocString(L""); + + return hr; } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
I _think_ this pipeline failure is due to `uiautomationcore:uiautomation:03dc The test prints too much data (33702 bytes)`, which if that is the case I'm going to have to fix that first I guess. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2458#note_27409
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/tests/uiautomation.c:
+{ + IUIAutomationElement *element; + HRESULT hr; + VARIANT v; + + hr = IUIAutomation_GetRootElement(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* NULL input argument. */ + hr = IUIAutomation_GetRootElement(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* + * UiaGetRootNode is the same as calling UiaNodeFromHandle with the UiaGetRootNode isn't called by this test.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/2458#note_27424
This merge request was closed by Connor McAdams. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2458
I'm going to go ahead and close this MR down, try to get the todos under control, and then open up a new one. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/2458#note_27430
participants (3)
-
Connor McAdams -
Connor McAdams (@cmcadams) -
Esme Povirk (@madewokherd)