uiautomationcore: Implement IUIAutomationElement::GetCachedPropertyValueEx.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2023-03-07 14:42:13 -05:00 committed by Alexandre Julliard
parent 29a4e096d7
commit d3d108f9b8
2 changed files with 297 additions and 99 deletions

View file

@ -10869,11 +10869,25 @@ static const struct prov_method_sequence get_cached_prop_val_seq2[] = {
{ 0 },
};
static const struct prov_method_sequence get_cached_prop_val_seq3[] = {
{ &Provider, PROV_GET_PROPERTY_VALUE },
NODE_CREATE_SEQ(&Provider_child),
{ &Provider, PROV_GET_PROPERTY_VALUE },
NODE_CREATE_SEQ(&Provider_child),
NODE_CREATE_SEQ(&Provider_child2),
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ControlTypePropertyId */
{ &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_ControlTypePropertyId */
{ 0 },
};
static void test_Element_cache_methods(IUIAutomation *uia_iface)
{
HWND hwnd = create_test_hwnd("test_Element_cache_methods class");
IUIAutomationElement *element, *element2;
IUIAutomationElement *element, *element2, *element3;
IUIAutomationCacheRequest *cache_req;
IUIAutomationElementArray *elem_arr;
int tmp_rt_id[2], i, len;
IUnknown *unk_ns;
HRESULT hr;
VARIANT v;
@ -10938,15 +10952,14 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface)
/* RuntimeId is currently unset, so we'll get the NotSupported value. */
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v));
if (SUCCEEDED(hr))
ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v));
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
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);
/* Attempting to get a cached value for a non-cached property. */
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_IsControlElementPropertyId, TRUE, &v);
todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&v));
VariantClear(&v);
@ -10961,10 +10974,9 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface)
ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq");
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v));
if (SUCCEEDED(hr))
check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v));
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v));
check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v));
VariantClear(&v);
IUIAutomationElement_Release(element2);
@ -10983,22 +10995,116 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface)
ok_method_sequence(get_cached_prop_val_seq2, "get_cached_prop_val_seq2");
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v));
if (SUCCEEDED(hr))
check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v));
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v));
check_runtime_id(Provider_child.runtime_id, ARRAY_SIZE(Provider_child.runtime_id), V_ARRAY(&v));
VariantClear(&v);
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_IsControlElementPropertyId, TRUE, &v);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v));
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v));
VariantClear(&v);
IUIAutomationElement_Release(element2);
IUIAutomationCacheRequest_Release(cache_req);
IUIAutomationElement_Release(element);
ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
/* Test cached UIAutomationType_Element properties. */
element = create_test_element_from_hwnd(uia_iface, hwnd, TRUE);
cache_req = NULL;
hr = IUIAutomation_CreateCacheRequest(uia_iface, &cache_req);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!cache_req, "cache_req == NULL\n");
/* UIAutomationType_Element property. */
hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_LabeledByPropertyId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* UIAutomationType_ElementArray property. */
hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_ControllerForPropertyId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
element2 = NULL;
hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!element2, "element2 == NULL\n");
ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
tmp_rt_id[0] = UIA_RUNTIME_ID_PREFIX;
tmp_rt_id[1] = HandleToULong(hwnd);
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_RuntimeIdPropertyId, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&v));
check_runtime_id(tmp_rt_id, ARRAY_SIZE(tmp_rt_id), V_ARRAY(&v));
VariantClear(&v);
/* Cached IUIAutomationElement. */
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_LabeledByPropertyId, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v));
element3 = NULL;
hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElement, (void **)&element3);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!element3, "element3 == NULL\n");
VariantClear(&v);
hr = IUIAutomationElement_GetCurrentPropertyValueEx(element3, UIA_IsControlElementPropertyId, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v));
IUIAutomationElement_Release(element3);
VariantClear(&v);
/* Cached IUIAutomationElementArray. */
hr = IUIAutomationElement_GetCachedPropertyValueEx(element2, UIA_ControllerForPropertyId, TRUE, &v);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v));
hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElementArray, (void **)&elem_arr);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(!!elem_arr, "elem_arr == NULL\n");
VariantClear(&v);
hr = IUIAutomationElementArray_get_Length(elem_arr, &len);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(len == ARRAY_SIZE(uia_unk_arr_prop_val), "Unexpected length %d\n", len);
for (i = 0; i < ARRAY_SIZE(uia_unk_arr_prop_val); i++)
{
hr = IUIAutomationElementArray_GetElement(elem_arr, i, &element3);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
ok(!!element3, "element3 == NULL\n");
hr = IUIAutomationElement_GetCurrentPropertyValueEx(element3, UIA_ControlTypePropertyId, TRUE, &v);
ok(hr == S_OK, "elem[%d] Unexpected hr %#lx\n", i, hr);
ok(V_VT(&v) == VT_I4, "elem[%d] Unexpected VT %d\n", i, V_VT(&v));
ok(V_I4(&v) == uia_i4_prop_val, "elem[%d] Unexpected I4 %#lx\n", i, V_I4(&v));
IUIAutomationElement_Release(element3);
VariantClear(&v);
}
IUIAutomationElementArray_Release(elem_arr);
IUIAutomationCacheRequest_Release(cache_req);
/*
* Reference isn't released until the element holding the cache is
* destroyed.
*/
ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
IUIAutomationElement_Release(element2);
ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok_method_sequence(get_cached_prop_val_seq3, "get_cached_prop_val_seq3");
IUIAutomationElement_Release(element);
ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
IUnknown_Release(unk_ns);
DestroyWindow(hwnd);

View file

@ -1029,6 +1029,11 @@ static HRESULT create_uia_element_array_iface(IUIAutomationElementArray **iface,
return S_OK;
}
struct uia_cache_property {
int prop_id;
VARIANT prop_val;
};
/*
* IUIAutomationElement interface.
*/
@ -1038,6 +1043,9 @@ struct uia_element {
BOOL from_cui8;
HUIANODE node;
struct uia_cache_property *cached_props;
int cached_props_count;
};
static inline struct uia_element *impl_from_IUIAutomationElement9(IUIAutomationElement9 *iface)
@ -1079,6 +1087,15 @@ static ULONG WINAPI uia_element_Release(IUIAutomationElement9 *iface)
TRACE("%p, refcount %ld\n", element, ref);
if (!ref)
{
if (element->cached_props_count)
{
int i;
for (i = 0; i < element->cached_props_count; i++)
VariantClear(&element->cached_props[i].prop_val);
}
heap_free(element->cached_props);
UiaNodeRelease(element->node);
heap_free(element);
}
@ -1126,8 +1143,8 @@ static HRESULT WINAPI uia_element_FindAllBuildCache(IUIAutomationElement9 *iface
return E_NOTIMPL;
}
static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, SAFEARRAY *req_data,
BSTR tree_struct);
static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8,
struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct);
static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface, IUIAutomationCacheRequest *cache_req,
IUIAutomationElement **updated_elem)
{
@ -1152,7 +1169,7 @@ static HRESULT WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface
if (FAILED(hr))
return hr;
hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, sa, tree_struct);
hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, cache_req_struct, sa, tree_struct);
if (SUCCEEDED(hr))
*updated_elem = cache_elem;
@ -1168,45 +1185,78 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValue(IUIAutomationElement9
}
static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, HUIANODE node);
static HRESULT create_element_array_from_node_array(SAFEARRAY *sa, BOOL from_cui8,
IUIAutomationElementArray **out_elem_arr)
static HRESULT get_element_variant_from_node_variant(VARIANT *var, BOOL from_cui8, int prop_type)
{
struct uia_element_array *elem_arr_data;
IUIAutomationElementArray *elem_arr;
LONG idx, lbound, elems, i;
HUIANODE node;
HRESULT hr;
*out_elem_arr = NULL;
hr = get_safearray_bounds(sa, &lbound, &elems);
if (FAILED(hr))
return hr;
/* ReservedNotSupported interface, just return it. */
if (V_VT(var) == VT_UNKNOWN)
return S_OK;
hr = create_uia_element_array_iface(&elem_arr, elems);
if (FAILED(hr))
return hr;
elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr);
for (i = 0; i < elems; i++)
if (prop_type & UIAutomationType_Array)
{
HUIANODE node;
struct uia_element_array *elem_arr_data;
IUIAutomationElementArray *elem_arr;
LONG idx, lbound, elems, i;
idx = lbound + i;
hr = SafeArrayGetElement(sa, &idx, &node);
if (FAILED(hr))
break;
hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node);
hr = get_safearray_bounds(V_ARRAY(var), &lbound, &elems);
if (FAILED(hr))
{
UiaNodeRelease(node);
break;
VariantClear(var);
return hr;
}
}
if (SUCCEEDED(hr))
*out_elem_arr = elem_arr;
hr = create_uia_element_array_iface(&elem_arr, elems);
if (FAILED(hr))
{
VariantClear(var);
return hr;
}
elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr);
for (i = 0; i < elems; i++)
{
idx = lbound + i;
hr = SafeArrayGetElement(V_ARRAY(var), &idx, &node);
if (FAILED(hr))
break;
hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node);
if (FAILED(hr))
{
UiaNodeRelease(node);
break;
}
}
VariantClear(var);
if (SUCCEEDED(hr))
{
V_VT(var) = VT_UNKNOWN;
V_UNKNOWN(var) = (IUnknown *)elem_arr;
}
else
IUIAutomationElementArray_Release(elem_arr);
}
else
IUIAutomationElementArray_Release(elem_arr);
{
IUIAutomationElement *out_elem;
hr = UiaHUiaNodeFromVariant(var, &node);
VariantClear(var);
if (FAILED(hr))
return hr;
hr = create_uia_element(&out_elem, from_cui8, node);
if (SUCCEEDED(hr))
{
V_VT(var) = VT_UNKNOWN;
V_UNKNOWN(var) = (IUnknown *)out_elem;
}
else
UiaNodeRelease(node);
}
return hr;
}
@ -1234,53 +1284,8 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement
if (FAILED(hr))
return hr;
switch (prop_info->type)
{
case UIAutomationType_Element:
{
IUIAutomationElement *out_elem;
HUIANODE node;
if (V_VT(ret_val) == VT_UNKNOWN)
break;
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;
}
else
UiaNodeRelease(node);
break;
}
case UIAutomationType_ElementArray:
{
IUIAutomationElementArray *out_elem_arr;
if (V_VT(ret_val) == VT_UNKNOWN)
break;
hr = create_element_array_from_node_array(V_ARRAY(ret_val), element->from_cui8, &out_elem_arr);
VariantClear(ret_val);
if (SUCCEEDED(hr))
{
V_VT(ret_val) = VT_UNKNOWN;
V_UNKNOWN(ret_val) = (IUnknown *)out_elem_arr;
}
break;
}
default:
break;
}
if ((prop_info->type == UIAutomationType_Element) || (prop_info->type == UIAutomationType_ElementArray))
hr = get_element_variant_from_node_variant(ret_val, element->from_cui8, prop_info->type);
return hr;
}
@ -1292,11 +1297,38 @@ static HRESULT WINAPI uia_element_GetCachedPropertyValue(IUIAutomationElement9 *
return E_NOTIMPL;
}
static int __cdecl uia_cached_property_id_compare(const void *a, const void *b)
{
const PROPERTYID *prop_id = a;
const struct uia_cache_property *cache_prop = b;
return ((*prop_id) > cache_prop->prop_id) - ((*prop_id) < cache_prop->prop_id);
}
static HRESULT WINAPI uia_element_GetCachedPropertyValueEx(IUIAutomationElement9 *iface, PROPERTYID prop_id,
BOOL ignore_default, VARIANT *ret_val)
{
FIXME("%p: stub\n", iface);
return E_NOTIMPL;
struct uia_element *element = impl_from_IUIAutomationElement9(iface);
struct uia_cache_property *cache_prop = NULL;
TRACE("%p, %d, %d, %p\n", iface, prop_id, ignore_default, ret_val);
if (!ret_val)
return E_POINTER;
VariantInit(ret_val);
if (!uia_prop_info_from_id(prop_id) || !element->cached_props)
return E_INVALIDARG;
if (!ignore_default)
FIXME("Default values currently unimplemented\n");
if (!(cache_prop = bsearch(&prop_id, element->cached_props, element->cached_props_count, sizeof(*cache_prop),
uia_cached_property_id_compare)))
return E_INVALIDARG;
VariantCopy(ret_val, &cache_prop->prop_val);
return S_OK;
}
static HRESULT WINAPI uia_element_GetCurrentPatternAs(IUIAutomationElement9 *iface, PATTERNID pattern_id,
@ -2144,9 +2176,19 @@ static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8,
return S_OK;
}
static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, SAFEARRAY *req_data,
BSTR tree_struct)
static int __cdecl uia_compare_cache_props(const void *a, const void *b)
{
struct uia_cache_property *prop1 = (struct uia_cache_property *)a;
struct uia_cache_property *prop2 = (struct uia_cache_property *)b;
return (prop1->prop_id > prop2->prop_id) - (prop1->prop_id < prop2->prop_id);
}
static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8,
struct UiaCacheRequest *cache_req, SAFEARRAY *req_data, BSTR tree_struct)
{
IUIAutomationElement *element = NULL;
struct uia_element *elem_data;
HUIANODE node;
LONG idx[2];
HRESULT hr;
@ -2165,11 +2207,61 @@ static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, B
goto exit;
VariantClear(&v);
hr = create_uia_element(iface, from_cui8, node);
hr = create_uia_element(&element, from_cui8, node);
if (FAILED(hr))
goto exit;
elem_data = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)element);
if (cache_req->cProperties)
{
LONG i;
elem_data->cached_props = heap_alloc_zero(sizeof(*elem_data->cached_props) * cache_req->cProperties);
if (!elem_data->cached_props)
{
hr = E_OUTOFMEMORY;
goto exit;
}
elem_data->cached_props_count = cache_req->cProperties;
for (i = 0; i < cache_req->cProperties; i++)
{
const struct uia_prop_info *prop_info = uia_prop_info_from_id(cache_req->pProperties[i]);
elem_data->cached_props[i].prop_id = prop_info->prop_id;
idx[0] = 0;
idx[1] = 1 + i;
hr = SafeArrayGetElement(req_data, idx, &elem_data->cached_props[i].prop_val);
if (FAILED(hr))
goto exit;
if ((prop_info->type == UIAutomationType_Element) || (prop_info->type == UIAutomationType_ElementArray))
{
hr = get_element_variant_from_node_variant(&elem_data->cached_props[i].prop_val, from_cui8,
prop_info->type);
if (FAILED(hr))
goto exit;
}
}
/*
* Sort the array of cached properties by property ID so that we can
* access the values with bsearch.
*/
qsort(elem_data->cached_props, elem_data->cached_props_count, sizeof(*elem_data->cached_props),
uia_compare_cache_props);
}
*iface = element;
exit:
if (FAILED(hr))
{
WARN("Failed to create element from cache request, hr %#lx\n", hr);
if (element)
IUIAutomationElement_Release(element);
}
SysFreeString(tree_struct);
return hr;