From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 138 +++++++++-- dlls/uiautomationcore/uia_com_client.c | 254 ++++++++++++++------- 2 files changed, 295 insertions(+), 97 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 41bd168e3c6..6b988504531 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -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); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 2b499fa8bc5..6309d5a952c 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -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; - - hr = create_uia_element_array_iface(&elem_arr, elems); - if (FAILED(hr)) - return hr; + /* ReservedNotSupported interface, just return it. */ + if (V_VT(var) == VT_UNKNOWN) + return S_OK;
- 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); + hr = get_safearray_bounds(V_ARRAY(var), &lbound, &elems); if (FAILED(hr)) - break; + { + VariantClear(var); + return hr; + }
- hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node); + hr = create_uia_element_array_iface(&elem_arr, elems); if (FAILED(hr)) { - UiaNodeRelease(node); - break; + VariantClear(var); + return hr; } - }
- if (SUCCEEDED(hr)) - *out_elem_arr = elem_arr; + 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;