From: Connor McAdams cmcadams@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@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);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@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);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@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);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@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)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@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) {
From: Connor McAdams cmcadams@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@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; } }
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.
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.
This merge request was closed by Connor McAdams.
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.