uiautomationcore: Implement IUIAutomationElement::GetCurrentPropertyValueEx.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2023-02-02 13:54:54 -05:00 committed by Alexandre Julliard
parent 2d8bf67b90
commit 6254f6635c
2 changed files with 173 additions and 29 deletions

View file

@ -4504,7 +4504,7 @@ static const struct prov_method_sequence node_from_prov8[] = {
{ 0 }
};
static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v);
static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v, BOOL from_com);
static DWORD WINAPI uia_node_from_provider_test_com_thread(LPVOID param)
{
HUIANODE node = param;
@ -4523,7 +4523,7 @@ static DWORD WINAPI uia_node_from_provider_test_com_thread(LPVOID param)
hr = UiaGetPropertyValue(node, UIA_ProcessIdPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ProcessIdPropertyId, UIAutomationType_Int, &v);
check_uia_prop_val(UIA_ProcessIdPropertyId, UIAutomationType_Int, &v, FALSE);
/*
* When retrieving a UIAutomationType_Element property, if UseComThreading
@ -4537,7 +4537,7 @@ static DWORD WINAPI uia_node_from_provider_test_com_thread(LPVOID param)
Provider_child.expected_tid = Provider.expected_tid;
hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v);
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v, FALSE);
/* Unset ProviderOptions_UseComThreading. */
Provider_child.prov_opts = ProviderOptions_ServerSideProvider;
@ -4549,7 +4549,7 @@ static DWORD WINAPI uia_node_from_provider_test_com_thread(LPVOID param)
* called on the current thread.
*/
Provider_child.expected_tid = GetCurrentThreadId();
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v);
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v, FALSE);
CoUninitialize();
@ -4982,7 +4982,7 @@ static void check_uia_rect_val_(VARIANT *v, struct UiaRect *rect, const char *fi
ok_(file, line)(tmp[3] == rect->height, "Unexpected height value %f, expected %f\n", tmp[3], rect->height);
}
static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v)
static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v, BOOL from_com)
{
LONG idx;
@ -5053,22 +5053,40 @@ static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, V
HRESULT hr;
VARIANT v1;
#ifdef _WIN64
ok(V_VT(v) == VT_I8, "Unexpected VT %d\n", V_VT(v));
tmp_node = (HUIANODE)V_I8(v);
#else
ok(V_VT(v) == VT_I4, "Unexpected VT %d\n", V_VT(v));
tmp_node = (HUIANODE)V_I4(v);
#endif
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
if (from_com)
{
IUIAutomationElement *elem;
ok(V_VT(v) == VT_UNKNOWN, "Unexpected VT %d\n", V_VT(v));
hr = IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IUIAutomationElement, (void **)&elem);
VariantClear(v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
hr = IUIAutomationElement_GetCurrentPropertyValueEx(elem, UIA_ControlTypePropertyId, TRUE, &v1);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
IUIAutomationElement_Release(elem);
}
else
{
#ifdef _WIN64
ok(V_VT(v) == VT_I8, "Unexpected VT %d\n", V_VT(v));
tmp_node = (HUIANODE)V_I8(v);
#else
ok(V_VT(v) == VT_I4, "Unexpected VT %d\n", V_VT(v));
tmp_node = (HUIANODE)V_I4(v);
#endif
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(UiaNodeRelease(tmp_node), "Failed to release node\n");
}
hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(V_VT(&v1) == VT_I4, "Unexpected VT %d\n", V_VT(&v1));
ok(V_I4(&v1) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v1));
ok(UiaNodeRelease(tmp_node), "Failed to release node\n");
ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
ok_method_sequence(get_elem_prop_seq, NULL);
break;
}
@ -5214,7 +5232,7 @@ static void test_UiaGetPropertyValue(void)
winetest_push_context("prop_id %d", prop_id);
hr = UiaGetPropertyValue(node, prop_id, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
check_uia_prop_val(prop_id, elem_prop->type, &v);
check_uia_prop_val(prop_id, elem_prop->type, &v, FALSE);
/*
* Some properties have special behavior if an invalid value is
@ -5973,7 +5991,7 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param)
hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v, FALSE);
/*
* On Windows, nested providers are always called on a separate thread if
@ -6132,7 +6150,7 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param)
hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v, FALSE);
/* Nodes returned from a nested node will be tracked and disconnectable. */
Provider_child.prov_opts = ProviderOptions_ServerSideProvider;
@ -6225,7 +6243,7 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param)
hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v, FALSE);
/* Finally, disconnect node. */
Provider.hwnd = hwnd;
@ -6447,7 +6465,7 @@ static void test_UiaNodeFromHandle(const char *name)
Provider.expected_tid = GetCurrentThreadId();
hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v);
check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v, FALSE);
/* UIAutomationType_Element properties will return a normal node. */
Provider_child.prov_opts = ProviderOptions_ServerSideProvider;
@ -9642,12 +9660,22 @@ static HWND create_test_hwnd(const char *class_name)
0, 0, 100, 100, NULL, NULL, NULL, NULL);
}
static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd)
static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd, BOOL block_hwnd_provs)
{
IUIAutomationElement *element;
HRESULT hr;
VARIANT v;
if (block_hwnd_provs)
{
SET_EXPECT(prov_callback_base_hwnd);
SET_EXPECT(prov_callback_nonclient);
base_hwnd_prov = proxy_prov = parent_proxy_prov = nc_prov = NULL;
UiaRegisterProviderCallback(test_uia_provider_callback);
}
else
UiaRegisterProviderCallback(NULL);
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE);
prov_root = &Provider.IRawElementProviderSimple_iface;
SET_EXPECT(winproc_GETOBJECT_UiaRoot);
@ -9659,19 +9687,31 @@ static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_if
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0;
if (block_hwnd_provs)
{
CHECK_CALLED(prov_callback_base_hwnd);
CHECK_CALLED(prov_callback_nonclient);
}
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(), hwnd);
check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", FALSE);
check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE);
check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE);
if (!block_hwnd_provs)
{
check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", FALSE);
check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE);
check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE);
}
else
check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE);
VariantClear(&v);
}
ok_method_sequence(node_from_hwnd2, "create_test_element");
UiaRegisterProviderCallback(NULL);
return element;
}
@ -9683,7 +9723,7 @@ static void test_ElementFromHandle(IUIAutomation *uia_iface, BOOL is_cui8)
IUIAutomationElement *element;
HRESULT hr;
element = create_test_element_from_hwnd(uia_iface, hwnd);
element = create_test_element_from_hwnd(uia_iface, hwnd, FALSE);
hr = IUIAutomationElement_QueryInterface(element, &IID_IUIAutomationElement2, (void **)&element_2);
if (is_cui8)
{
@ -9702,6 +9742,74 @@ static void test_ElementFromHandle(IUIAutomation *uia_iface, BOOL is_cui8)
prov_root = NULL;
}
static void test_Element_GetPropertyValue(IUIAutomation *uia_iface)
{
HWND hwnd = create_test_hwnd("test_Element_GetPropertyValue class");
const struct uia_element_property *elem_prop;
IUIAutomationElement *element;
IUnknown *unk_ns;
int i, prop_id;
HRESULT hr;
VARIANT v;
element = create_test_element_from_hwnd(uia_iface, hwnd, TRUE);
Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface;
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
provider_add_child(&Provider, &Provider_child);
hr = UiaGetReservedNotSupportedValue(&unk_ns);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
VariantInit(&v);
for (i = 0; i < ARRAY_SIZE(element_properties); i++)
{
elem_prop = &element_properties[i];
/* Skip ElementArray properties for now. */
if (elem_prop->type == UIAutomationType_ElementArray)
continue;
Provider.ret_invalid_prop_type = FALSE;
VariantClear(&v);
if (!(prop_id = UiaLookupId(AutomationIdentifierType_Property, elem_prop->prop_guid)))
{
win_skip("No propertyId for GUID %s, skipping further tests.\n", debugstr_guid(elem_prop->prop_guid));
break;
}
winetest_push_context("Element prop_id %d", prop_id);
hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, prop_id, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
check_uia_prop_val(prop_id, elem_prop->type, &v, TRUE);
/*
* Some properties have special behavior if an invalid value is
* returned, skip them here.
*/
if (!elem_prop->skip_invalid)
{
Provider.ret_invalid_prop_type = TRUE;
hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, prop_id, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (SUCCEEDED(hr))
{
ok_method_sequence(get_prop_invalid_type_seq, NULL);
ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v));
ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v));
VariantClear(&v);
}
}
winetest_pop_context();
}
IUIAutomationElement_Release(element);
ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
DestroyWindow(hwnd);
UnregisterClassA("test_Element_GetPropertyValue class", NULL);
}
struct uia_com_classes {
const GUID *clsid;
const GUID *iid;
@ -9766,6 +9874,7 @@ static void test_CUIAutomation(void)
ok(!!uia_iface, "uia_iface == NULL\n");
test_ElementFromHandle(uia_iface, has_cui8);
test_Element_GetPropertyValue(uia_iface);
IUIAutomation_Release(uia_iface);
CoUninitialize();

View file

@ -134,11 +134,46 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValue(IUIAutomationElement9
return E_NOTIMPL;
}
static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, HUIANODE node);
static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement9 *iface, PROPERTYID prop_id,
BOOL ignore_default, VARIANT *ret_val)
{
FIXME("%p: stub\n", iface);
return E_NOTIMPL;
const struct uia_prop_info *prop_info = uia_prop_info_from_id(prop_id);
struct uia_element *element = impl_from_IUIAutomationElement9(iface);
HRESULT hr;
TRACE("%p, %d, %d, %p\n", iface, prop_id, ignore_default, ret_val);
if (!ignore_default)
FIXME("Default values currently unimplemented\n");
VariantInit(ret_val);
if (prop_info->type == UIAutomationType_ElementArray)
{
FIXME("ElementArray property types currently unsupported for IUIAutomationElement\n");
return E_NOTIMPL;
}
hr = UiaGetPropertyValue(element->node, prop_id, ret_val);
if ((prop_info->type == UIAutomationType_Element) && (V_VT(ret_val) != VT_UNKNOWN))
{
IUIAutomationElement *out_elem;
HUIANODE node;
hr = UiaHUiaNodeFromVariant(ret_val, &node);
VariantClear(ret_val);
if (FAILED(hr))
return hr;
hr = create_uia_element(&out_elem, element->from_cui8, node);
if (SUCCEEDED(hr))
{
V_VT(ret_val) = VT_UNKNOWN;
V_UNKNOWN(ret_val) = (IUnknown *)out_elem;
}
}
return hr;
}
static HRESULT WINAPI uia_element_GetCachedPropertyValue(IUIAutomationElement9 *iface, PROPERTYID prop_id,