Add support for `UIAutomationType_ElementArray` properties in the COM API, and begin adding support for creating elements from cache requests in the COM API.
-- v2: uiautomationcore: Implement IUIAutomationElement::BuildUpdatedCache. uiautomationcore/tests: Add tests for IUIAutomationElement caching methods. uiautomationcore: Implement IUIAutomation::CreateCacheRequest. uiautomationcore: Add support for element array properties in IUIAutomationElement::GetCurrentPropertyValueEx.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 8 ++++++++ dlls/uiautomationcore/uia_com_client.c | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index e6be0ce75a3..1c3f1d6c15c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -9851,7 +9851,15 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) hr = UiaGetReservedNotSupportedValue(&unk_ns); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ControlTypePropertyId, TRUE, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + VariantInit(&v); + V_VT(&v) = VT_BOOL; + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, 1, TRUE, &v); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&v)); + for (i = 0; i < ARRAY_SIZE(element_properties); i++) { elem_prop = &element_properties[i]; diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index a6320f15b0d..e5fdc049342 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -714,10 +714,16 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement
TRACE("%p, %d, %d, %p\n", iface, prop_id, ignore_default, ret_val);
+ if (!ret_val) + return E_POINTER; + + VariantInit(ret_val); + if (!prop_info) + return E_INVALIDARG; + 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");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 135 ++++++++++++-- dlls/uiautomationcore/uia_com_client.c | 207 ++++++++++++++++++++- 2 files changed, 316 insertions(+), 26 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 1c3f1d6c15c..a1d091e0074 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -5184,27 +5184,70 @@ static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, V }
case UIAutomationType_ElementArray: - ok(V_VT(v) == (VT_ARRAY | VT_UNKNOWN), "Unexpected VT %d\n", V_VT(v)); - if (V_VT(v) != (VT_ARRAY | VT_UNKNOWN)) - break; - ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + if (from_com) { - HUIANODE tmp_node; + IUIAutomationElementArray *elem_arr = NULL; HRESULT hr; - VARIANT v1; + int len; + + 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"); + if (!elem_arr) + { + VariantClear(v); + break; + }
- SafeArrayGetElement(V_ARRAY(v), &idx, &tmp_node); + 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);
- hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1); - ok(hr == S_OK, "node[%ld] Unexpected hr %#lx\n", idx, hr); - ok(V_VT(&v1) == VT_I4, "node[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); - ok(V_I4(&v1) == uia_i4_prop_val, "node[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1)); + for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + { + IUIAutomationElement *tmp_elem = NULL; + VARIANT v1; + + hr = IUIAutomationElementArray_GetElement(elem_arr, idx, &tmp_elem); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!tmp_elem, "tmp_elem == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(tmp_elem, UIA_ControlTypePropertyId, TRUE, &v1); + ok(hr == S_OK, "elem[%ld] Unexpected hr %#lx\n", idx, hr); + ok(V_VT(&v1) == VT_I4, "elem[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); + ok(V_I4(&v1) == uia_i4_prop_val, "elem[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1));
- ok(UiaNodeRelease(tmp_node), "Failed to release node[%ld]\n", idx); - VariantClear(&v1); + IUIAutomationElement_Release(tmp_elem); + VariantClear(&v1); + } + + IUIAutomationElementArray_Release(elem_arr); + } + else + { + ok(V_VT(v) == (VT_ARRAY | VT_UNKNOWN), "Unexpected VT %d\n", V_VT(v)); + if (V_VT(v) != (VT_ARRAY | VT_UNKNOWN)) + break; + + for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + { + HUIANODE tmp_node; + HRESULT hr; + VARIANT v1; + + SafeArrayGetElement(V_ARRAY(v), &idx, &tmp_node); + + hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1); + ok(hr == S_OK, "node[%ld] Unexpected hr %#lx\n", idx, hr); + ok(V_VT(&v1) == VT_I4, "node[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); + ok(V_I4(&v1) == uia_i4_prop_val, "node[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1)); + + ok(UiaNodeRelease(tmp_node), "Failed to release node[%ld]\n", idx); + VariantClear(&v1); + } }
VariantClear(v); @@ -9835,7 +9878,8 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) const struct uia_element_property *elem_prop; struct Provider_prop_override prop_override; IUIAutomationElement *element, *element2; - int i, prop_id, tmp_int; + IUIAutomationElementArray *element_arr; + int i, len, prop_id, tmp_int; struct UiaRect uia_rect; IUnknown *unk_ns; BSTR tmp_bstr; @@ -9864,10 +9908,6 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) { 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))) @@ -9902,6 +9942,63 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) winetest_pop_context(); }
+ /* Test IUIAutomationElementArray interface behavior. */ + Provider.ret_invalid_prop_type = FALSE; + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, 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)); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + element_arr = NULL; + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElementArray, (void **)&element_arr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!element_arr, "element_arr == NULL\n"); + VariantClear(&v); + + hr = IUIAutomationElementArray_get_Length(element_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); + + /* Invalid argument tests. */ + hr = IUIAutomationElementArray_get_Length(element_arr, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx\n", hr); + + element2 = (void *)0xdeadbeef; + hr = IUIAutomationElementArray_GetElement(element_arr, len, &element2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx\n", hr); + /* Pointer isn't cleared. */ + ok(!!element2, "element2 == NULL\n"); + + hr = IUIAutomationElementArray_GetElement(element_arr, -1, &element2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx\n", hr); + /* Pointer isn't cleared. */ + ok(!!element2, "element2 == NULL\n"); + + hr = IUIAutomationElementArray_GetElement(element_arr, 0, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx\n", hr); + + for (i = 0; i < len; i++) + { + element2 = NULL; + hr = IUIAutomationElementArray_GetElement(element_arr, i, &element2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!element2, "element2 == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ControlTypePropertyId, TRUE, &v); + ok(hr == S_OK, "element[%d] Unexpected hr %#lx\n", i, hr); + ok(V_VT(&v) == VT_I4, "element[%d] Unexpected VT %d\n", i, V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "element[%d] Unexpected I4 %#lx\n", i, V_I4(&v)); + + IUIAutomationElement_Release(element2); + VariantClear(&v); + } + + IUIAutomationElementArray_Release(element_arr); + 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_elem_arr_prop_seq, NULL); + /* * IUIAutomationElement_get_CurrentControlType tests. If the value * returned for UIA_ControlTypePropertyId is not a registered control diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index e5fdc049342..83205255c7c 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -593,6 +593,129 @@ static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condi return S_OK; }
+/* + * IUIAutomationElementArray interface. + */ +struct uia_element_array { + IUIAutomationElementArray IUIAutomationElementArray_iface; + LONG ref; + + IUIAutomationElement **elements; + int elements_count; +}; + +static inline struct uia_element_array *impl_from_IUIAutomationElementArray(IUIAutomationElementArray *iface) +{ + return CONTAINING_RECORD(iface, struct uia_element_array, IUIAutomationElementArray_iface); +} + +static HRESULT WINAPI uia_element_array_QueryInterface(IUIAutomationElementArray *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IUIAutomationElementArray)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationElementArray_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_element_array_AddRef(IUIAutomationElementArray *iface) +{ + struct uia_element_array *element = impl_from_IUIAutomationElementArray(iface); + ULONG ref = InterlockedIncrement(&element->ref); + + TRACE("%p, refcount %ld\n", element, ref); + return ref; +} + +static ULONG WINAPI uia_element_array_Release(IUIAutomationElementArray *iface) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + ULONG ref = InterlockedDecrement(&element_arr->ref); + + TRACE("%p, refcount %ld\n", element_arr, ref); + if (!ref) + { + if (element_arr->elements_count) + { + int i; + + for (i = 0; i < element_arr->elements_count; i++) + { + if (element_arr->elements[i]) + IUIAutomationElement_Release(element_arr->elements[i]); + } + } + heap_free(element_arr->elements); + heap_free(element_arr); + } + + return ref; +} + +static HRESULT WINAPI uia_element_array_get_Length(IUIAutomationElementArray *iface, int *out_length) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + + TRACE("%p, %p\n", iface, out_length); + + if (!out_length) + return E_POINTER; + + *out_length = element_arr->elements_count; + + return S_OK; +} + +static HRESULT WINAPI uia_element_array_GetElement(IUIAutomationElementArray *iface, int idx, + IUIAutomationElement **out_elem) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + + TRACE("%p, %p\n", iface, out_elem); + + if (!out_elem) + return E_POINTER; + + if ((idx < 0) || (idx >= element_arr->elements_count)) + return E_INVALIDARG; + + *out_elem = element_arr->elements[idx]; + IUIAutomationElement_AddRef(element_arr->elements[idx]); + + return S_OK; +} + +static const IUIAutomationElementArrayVtbl uia_element_array_vtbl = { + uia_element_array_QueryInterface, + uia_element_array_AddRef, + uia_element_array_Release, + uia_element_array_get_Length, + uia_element_array_GetElement, +}; + +static HRESULT create_uia_element_array_iface(IUIAutomationElementArray **iface, int elements_count) +{ + struct uia_element_array *element_arr = heap_alloc_zero(sizeof(*element_arr)); + + *iface = NULL; + if (!element_arr) + return E_OUTOFMEMORY; + + element_arr->IUIAutomationElementArray_iface.lpVtbl = &uia_element_array_vtbl; + element_arr->ref = 1; + element_arr->elements_count = elements_count; + if (!(element_arr->elements = heap_alloc_zero(sizeof(*element_arr->elements) * elements_count))) + { + heap_free(element_arr); + return E_OUTOFMEMORY; + } + + *iface = &element_arr->IUIAutomationElementArray_iface; + return S_OK; +} + /* * IUIAutomationElement interface. */ @@ -705,6 +828,49 @@ 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) +{ + struct uia_element_array *elem_arr_data; + IUIAutomationElementArray *elem_arr; + LONG idx, lbound, elems, i; + 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; + + elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr); + for (i = 0; i < elems; i++) + { + HUIANODE node; + + idx = lbound + i; + hr = SafeArrayGetElement(sa, &idx, &node); + if (FAILED(hr)) + break; + + hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node); + if (FAILED(hr)) + { + UiaNodeRelease(node); + break; + } + } + + if (SUCCEEDED(hr)) + *out_elem_arr = elem_arr; + else + IUIAutomationElementArray_Release(elem_arr); + + return hr; +} + static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement9 *iface, PROPERTYID prop_id, BOOL ignore_default, VARIANT *ret_val) { @@ -724,18 +890,20 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement if (!ignore_default) FIXME("Default values currently unimplemented\n");
- 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)) + 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)) @@ -747,6 +915,31 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement 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; }
return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 166 ++++++++++++ dlls/uiautomationcore/uia_com_client.c | 290 +++++++++++++++++++-- 2 files changed, 428 insertions(+), 28 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a1d091e0074..e9a9723ab20 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10560,6 +10560,171 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationBoolCondition_Release(bool_cond); }
+static void test_CUIAutomation_cache_request_iface(IUIAutomation *uia_iface) +{ + IUIAutomationPropertyCondition *prop_cond; + enum PropertyConditionFlags prop_flags; + IUIAutomationCacheRequest *cache_req; + enum AutomationElementMode elem_mode; + IUIAutomationCondition *cond, *cond2; + IUIAutomationNotCondition *not_cond; + enum TreeScope scope; + PROPERTYID prop_id; + HRESULT hr; + VARIANT v; + + hr = IUIAutomation_CreateCacheRequest(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* + * CreateCacheRequest returns an IUIAutomationCacheRequest with the + * default cache request values set. + */ + 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"); + + /* + * TreeScope tests. + */ + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(scope == TreeScope_Element, "Unexpected scope %#x\n", scope); + + /* Set it to something invalid. */ + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Parent); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Ancestors); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Parent | TreeScope_Element); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Ancestors | TreeScope_Element); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, ~(TreeScope_SubTree | TreeScope_Parent | TreeScope_Ancestors)); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Invalid values don't change anything. */ + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(scope == TreeScope_Element, "Unexpected scope %#x\n", scope); + + /* Now set it to TreeScope_Children. */ + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Children); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(scope == TreeScope_Children, "Unexpected scope %#x\n", scope); + + /* + * TreeFilter tests. + */ + cond = NULL; + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, (IUIAutomationCondition *)&Object); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + + /* Default IUIAutomationCacheRequest has the ControlView condition. */ + cond = NULL; + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationNotCondition, (void **)¬_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!not_cond, "not_cond == NULL\n"); + + cond = NULL; + hr = IUIAutomationNotCondition_GetChild(not_cond, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationPropertyCondition, (void **)&prop_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!prop_cond, "prop_cond == NULL\n"); + + hr = IUIAutomationPropertyCondition_get_PropertyId(prop_cond, &prop_id); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(prop_id == UIA_IsControlElementPropertyId, "Unexpected prop_id %d.\n", prop_id); + + VariantInit(&v); + hr = IUIAutomationPropertyCondition_get_PropertyValue(prop_cond, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + VariantClear(&v); + + hr = IUIAutomationPropertyCondition_get_PropertyConditionFlags(prop_cond, &prop_flags); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(prop_flags == PropertyConditionFlags_None, "Unexpected flags %#x.\n", prop_flags); + + IUIAutomationPropertyCondition_Release(prop_cond); + IUIAutomationNotCondition_Release(not_cond); + + /* Set a new TreeFilter condition. */ + cond = NULL; + hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, &cond2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond2, "cond2 == NULL\n"); + ok(iface_cmp((IUnknown *)cond, (IUnknown *)cond2), "cond != cond2\n"); + IUIAutomationCondition_Release(cond); + IUIAutomationCondition_Release(cond2); + + /* + * AutomationElementMode tests. + */ + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + elem_mode = 0; + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, &elem_mode); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(elem_mode == AutomationElementMode_Full, "Unexpected element mode %#x\n", elem_mode); + + /* Invalid value - maximum is AutomationElementMode_Full, 0x01. */ + hr = IUIAutomationCacheRequest_put_AutomationElementMode(cache_req, 0x02); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_AutomationElementMode(cache_req, AutomationElementMode_None); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + elem_mode = 0; + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, &elem_mode); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(elem_mode == AutomationElementMode_None, "Unexpected element mode %#x\n", elem_mode); + + IUIAutomationCacheRequest_Release(cache_req); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -10660,6 +10825,7 @@ static void test_CUIAutomation(void)
test_CUIAutomation_condition_ifaces(uia_iface); test_CUIAutomation_value_conversion(uia_iface); + test_CUIAutomation_cache_request_iface(uia_iface); test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface);
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 83205255c7c..96eaee412b4 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -593,6 +593,264 @@ static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condi return S_OK; }
+static HRESULT create_control_view_condition_iface(IUIAutomationCondition **out_condition) +{ + IUIAutomationCondition *prop_cond, *not_cond; + HRESULT hr; + VARIANT v; + + if (!out_condition) + return E_POINTER; + + *out_condition = NULL; + + VariantInit(&v); + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + hr = create_uia_property_condition_iface(&prop_cond, UIA_IsControlElementPropertyId, v, PropertyConditionFlags_None); + if (FAILED(hr)) + return hr; + + hr = create_uia_not_condition_iface(¬_cond, prop_cond); + if (FAILED(hr)) + { + IUIAutomationCondition_Release(prop_cond); + return hr; + } + + *out_condition = not_cond; + + return S_OK; +} + +/* + * IUIAutomationCacheRequest interface. + */ +struct uia_cache_request { + IUIAutomationCacheRequest IUIAutomationCacheRequest_iface; + LONG ref; + + IUIAutomationCondition *view_condition; + struct UiaCacheRequest cache_req; +}; + +static inline struct uia_cache_request *impl_from_IUIAutomationCacheRequest(IUIAutomationCacheRequest *iface) +{ + return CONTAINING_RECORD(iface, struct uia_cache_request, IUIAutomationCacheRequest_iface); +} + +static HRESULT WINAPI uia_cache_request_QueryInterface(IUIAutomationCacheRequest *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationCacheRequest) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationCacheRequest_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_cache_request_AddRef(IUIAutomationCacheRequest *iface) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + ULONG ref = InterlockedIncrement(&uia_cache_request->ref); + + TRACE("%p, refcount %ld\n", uia_cache_request, ref); + return ref; +} + +static ULONG WINAPI uia_cache_request_Release(IUIAutomationCacheRequest *iface) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + ULONG ref = InterlockedDecrement(&uia_cache_request->ref); + + TRACE("%p, refcount %ld\n", uia_cache_request, ref); + + if (!ref) + { + IUIAutomationCondition_Release(uia_cache_request->view_condition); + heap_free(uia_cache_request); + } + + return ref; +} + +static HRESULT WINAPI uia_cache_request_AddProperty(IUIAutomationCacheRequest *iface, PROPERTYID prop_id) +{ + FIXME("%p, %d: stub\n", iface, prop_id); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_AddPattern(IUIAutomationCacheRequest *iface, PATTERNID pattern_id) +{ + FIXME("%p, %d: stub\n", iface, pattern_id); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_Clone(IUIAutomationCacheRequest *iface, IUIAutomationCacheRequest **out_req) +{ + FIXME("%p, %p: stub\n", iface, out_req); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_get_TreeScope(IUIAutomationCacheRequest *iface, enum TreeScope *scope) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, scope); + + if (!scope) + return E_POINTER; + + *scope = uia_cache_request->cache_req.Scope; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_TreeScope(IUIAutomationCacheRequest *iface, enum TreeScope scope) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %#x\n", iface, scope); + + if (!scope || (scope & ~TreeScope_SubTree)) + return E_INVALIDARG; + + if ((scope & TreeScope_Children) || (scope & TreeScope_Descendants)) + { + FIXME("Unimplemented TreeScope %#x\n", scope); + return E_NOTIMPL; + } + + uia_cache_request->cache_req.Scope = scope; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_get_TreeFilter(IUIAutomationCacheRequest *iface, + IUIAutomationCondition **filter) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, filter); + + if (!filter) + return E_POINTER; + + IUIAutomationCondition_AddRef(uia_cache_request->view_condition); + *filter = uia_cache_request->view_condition; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_TreeFilter(IUIAutomationCacheRequest *iface, IUIAutomationCondition *filter) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + struct UiaCondition *cond_struct; + HRESULT hr; + + TRACE("%p, %p\n", iface, filter); + + if (!filter) + return E_POINTER; + + hr = get_uia_condition_struct_from_iface(filter, &cond_struct); + if (FAILED(hr)) + return hr; + + uia_cache_request->cache_req.pViewCondition = cond_struct; + IUIAutomationCondition_Release(uia_cache_request->view_condition); + uia_cache_request->view_condition = filter; + IUIAutomationCondition_AddRef(filter); + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_get_AutomationElementMode(IUIAutomationCacheRequest *iface, + enum AutomationElementMode *mode) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, mode); + + if (!mode) + return E_POINTER; + + *mode = uia_cache_request->cache_req.automationElementMode; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_AutomationElementMode(IUIAutomationCacheRequest *iface, + enum AutomationElementMode mode) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %d\n", iface, mode); + + if ((mode != AutomationElementMode_Full) && (mode != AutomationElementMode_None)) + return E_INVALIDARG; + + if (mode == AutomationElementMode_None) + { + FIXME("AutomationElementMode_None unsupported\n"); + return E_NOTIMPL; + } + + uia_cache_request->cache_req.automationElementMode = mode; + + return S_OK; +} + +static const IUIAutomationCacheRequestVtbl uia_cache_request_vtbl = { + uia_cache_request_QueryInterface, + uia_cache_request_AddRef, + uia_cache_request_Release, + uia_cache_request_AddProperty, + uia_cache_request_AddPattern, + uia_cache_request_Clone, + uia_cache_request_get_TreeScope, + uia_cache_request_put_TreeScope, + uia_cache_request_get_TreeFilter, + uia_cache_request_put_TreeFilter, + uia_cache_request_get_AutomationElementMode, + uia_cache_request_put_AutomationElementMode, +}; + +static HRESULT create_uia_cache_request_iface(IUIAutomationCacheRequest **out_cache_req) +{ + struct uia_cache_request *uia_cache_request; + IUIAutomationCondition *view_condition; + HRESULT hr; + + if (!out_cache_req) + return E_POINTER; + + *out_cache_req = NULL; + hr = create_control_view_condition_iface(&view_condition); + if (FAILED(hr)) + return hr; + + uia_cache_request = heap_alloc_zero(sizeof(*uia_cache_request)); + if (!uia_cache_request) + { + IUIAutomationCondition_Release(view_condition); + return E_OUTOFMEMORY; + } + + uia_cache_request->IUIAutomationCacheRequest_iface.lpVtbl = &uia_cache_request_vtbl; + uia_cache_request->ref = 1; + + uia_cache_request->view_condition = view_condition; + get_uia_condition_struct_from_iface(view_condition, &uia_cache_request->cache_req.pViewCondition); + uia_cache_request->cache_req.Scope = TreeScope_Element; + uia_cache_request->cache_req.automationElementMode = AutomationElementMode_Full; + + *out_cache_req = &uia_cache_request->IUIAutomationCacheRequest_iface; + return S_OK; +} + /* * IUIAutomationElementArray interface. */ @@ -1969,34 +2227,9 @@ static HRESULT WINAPI uia_iface_get_RawViewCondition(IUIAutomation6 *iface, IUIA
static HRESULT WINAPI uia_iface_get_ControlViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - IUIAutomationCondition *prop_cond, *not_cond; - HRESULT hr; - VARIANT v; - TRACE("%p, %p\n", iface, out_condition);
- if (!out_condition) - return E_POINTER; - - *out_condition = NULL; - - VariantInit(&v); - V_VT(&v) = VT_BOOL; - V_BOOL(&v) = VARIANT_FALSE; - hr = create_uia_property_condition_iface(&prop_cond, UIA_IsControlElementPropertyId, v, PropertyConditionFlags_None); - if (FAILED(hr)) - return hr; - - hr = create_uia_not_condition_iface(¬_cond, prop_cond); - if (FAILED(hr)) - { - IUIAutomationCondition_Release(prop_cond); - return hr; - } - - *out_condition = not_cond; - - return S_OK; + return create_control_view_condition_iface(out_condition); }
static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) @@ -2007,8 +2240,9 @@ static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface,
static HRESULT WINAPI uia_iface_CreateCacheRequest(IUIAutomation6 *iface, IUIAutomationCacheRequest **out_cache_req) { - FIXME("%p, %p: stub\n", iface, out_cache_req); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_cache_req); + + return create_uia_cache_request_iface(out_cache_req); }
static HRESULT WINAPI uia_iface_CreateTrueCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index e9a9723ab20..18d7278bba0 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10725,6 +10725,168 @@ static void test_CUIAutomation_cache_request_iface(IUIAutomation *uia_iface) IUIAutomationCacheRequest_Release(cache_req); }
+static const struct prov_method_sequence get_elem_cache_seq[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, + NODE_CREATE_SEQ(&Provider_child), + { 0 }, +}; + +static const struct prov_method_sequence get_cached_prop_val_seq[] = { + { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, + { 0 }, +}; + +static const struct prov_method_sequence get_cached_prop_val_seq2[] = { + { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_IsControlElementPropertyId */ + { 0 }, +}; + +static void test_Element_cache_methods(IUIAutomation *uia_iface) +{ + HWND hwnd = create_test_hwnd("test_Element_cache_methods class"); + IUIAutomationElement *element, *element2; + IUIAutomationCacheRequest *cache_req; + IUnknown *unk_ns; + 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); + + 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"); + + /* + * Run these tests on Provider_child, it doesn't have an HWND so it will + * get UIA_RuntimeIdPropertyId from GetRuntimeId. + */ + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, 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)); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok_method_sequence(get_elem_cache_seq, "get_elem_cache_seq"); + + IUIAutomationElement_Release(element); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElement, (void **)&element); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element, "element == NULL\n"); + VariantClear(&v); + + /* + * Passing in an invalid COM interface for IUIAutomationCacheRequest will + * cause an access violation on Windows. + */ + if (0) + { + IUIAutomationElement_BuildUpdatedCache(element, (IUIAutomationCacheRequest *)&Object, &element2); + } + + hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, NULL); + todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + element2 = (void *)0xdeadbeef; + hr = IUIAutomationElement_BuildUpdatedCache(element, NULL, &element2); + todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!element2, "element2 != NULL\n"); + + /* + * Test cached property values. The default IUIAutomationCacheRequest + * always caches UIA_RuntimeIdPropertyId. + */ + element2 = NULL; + hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!element2, "element2 == NULL\n"); + ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq"); + + if (element2) + { + /* 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)); + 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(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&v)); + VariantClear(&v); + + IUIAutomationElement_Release(element2); + } + + /* RuntimeId is now set. */ + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; + element2 = NULL; + hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!element2, "element2 == NULL\n"); + ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq"); + + if (element2) + { + 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)); + VariantClear(&v); + IUIAutomationElement_Release(element2); + } + + /* + * Add UIA_IsControlElementPropertyId to the list of cached property + * values. + */ + hr = IUIAutomationCacheRequest_AddProperty(cache_req, UIA_IsControlElementPropertyId); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadb33f; + element2 = NULL; + hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!element2, "element2 == NULL\n"); + ok_method_sequence(get_cached_prop_val_seq2, "get_cached_prop_val_seq2"); + + if (element2) + { + 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)); + 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)); + VariantClear(&v); + + IUIAutomationElement_Release(element2); + } + + IUIAutomationElement_Release(element); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + IUIAutomationCacheRequest_Release(cache_req); + IUnknown_Release(unk_ns); + + DestroyWindow(hwnd); + UnregisterClassA("test_Element_cache_methods class", NULL); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -10828,6 +10990,7 @@ static void test_CUIAutomation(void) test_CUIAutomation_cache_request_iface(uia_iface); test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface); + test_Element_cache_methods(uia_iface);
IUIAutomation_Release(uia_iface); CoUninitialize();
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 89 ++++++++++------------ dlls/uiautomationcore/uia_com_client.c | 80 ++++++++++++++++++- 2 files changed, 118 insertions(+), 51 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 18d7278bba0..95e4de1b106 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10792,12 +10792,12 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) }
hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, NULL); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
element2 = (void *)0xdeadbeef; hr = IUIAutomationElement_BuildUpdatedCache(element, NULL, &element2); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!element2, "element2 != NULL\n"); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!element2, "element2 != NULL\n");
/* * Test cached property values. The default IUIAutomationCacheRequest @@ -10805,47 +10805,41 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) */ element2 = NULL; hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!element2, "element2 == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element2, "element2 == NULL\n"); ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq");
- if (element2) - { - /* 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)); - VariantClear(&v); + /* 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)); + 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(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&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(V_VT(&v) == VT_EMPTY, "Unexpected vt %d\n", V_VT(&v)); + VariantClear(&v);
- IUIAutomationElement_Release(element2); - } + IUIAutomationElement_Release(element2);
/* RuntimeId is now set. */ Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadbeef; element2 = NULL; hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!element2, "element2 == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element2, "element2 == NULL\n"); ok_method_sequence(get_cached_prop_val_seq, "get_cached_prop_val_seq");
- if (element2) - { - 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)); - VariantClear(&v); - IUIAutomationElement_Release(element2); - } + 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)); + VariantClear(&v); + IUIAutomationElement_Release(element2);
/* * Add UIA_IsControlElementPropertyId to the list of cached property @@ -10857,26 +10851,23 @@ static void test_Element_cache_methods(IUIAutomation *uia_iface) Provider_child.runtime_id[0] = Provider_child.runtime_id[1] = 0xdeadb33f; element2 = NULL; hr = IUIAutomationElement_BuildUpdatedCache(element, cache_req, &element2); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!element2, "element2 == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element2, "element2 == NULL\n"); ok_method_sequence(get_cached_prop_val_seq2, "get_cached_prop_val_seq2");
- if (element2) - { - 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)); - VariantClear(&v); + 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)); + 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)); - 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)); + VariantClear(&v);
- IUIAutomationElement_Release(element2); - } + IUIAutomationElement_Release(element2);
IUIAutomationElement_Release(element); ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 96eaee412b4..44d715684c1 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -851,6 +851,24 @@ static HRESULT create_uia_cache_request_iface(IUIAutomationCacheRequest **out_ca return S_OK; }
+static HRESULT get_uia_cache_request_struct_from_iface(IUIAutomationCacheRequest *cache_request, + struct UiaCacheRequest **cache_req_struct) +{ + struct uia_cache_request *cache_req_data; + + *cache_req_struct = NULL; + if (!cache_request) + return E_POINTER; + + if (cache_request->lpVtbl != &uia_cache_request_vtbl) + return E_FAIL; + + cache_req_data = impl_from_IUIAutomationCacheRequest(cache_request); + *cache_req_struct = &cache_req_data->cache_req; + + return S_OK; +} + /* * IUIAutomationElementArray interface. */ @@ -1071,11 +1089,38 @@ 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 WINAPI uia_element_BuildUpdatedCache(IUIAutomationElement9 *iface, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **updated_elem) { - FIXME("%p: stub\n", iface); - return E_NOTIMPL; + struct uia_element *element = impl_from_IUIAutomationElement9(iface); + struct UiaCacheRequest *cache_req_struct; + IUIAutomationElement *cache_elem; + BSTR tree_struct; + SAFEARRAY *sa; + HRESULT hr; + + TRACE("%p, %p, %p\n", iface, cache_req, updated_elem); + + if (!updated_elem) + return E_POINTER; + + *updated_elem = NULL; + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + return hr; + + hr = UiaGetUpdatedCache(element->node, cache_req_struct, NormalizeState_None, NULL, &sa, &tree_struct); + if (FAILED(hr)) + return hr; + + hr = create_uia_element_from_cache_req(&cache_elem, element->from_cui8, sa, tree_struct); + if (SUCCEEDED(hr)) + *updated_elem = cache_elem; + + SafeArrayDestroy(sa); + return S_OK; }
static HRESULT WINAPI uia_element_GetCurrentPropertyValue(IUIAutomationElement9 *iface, PROPERTYID prop_id, @@ -2062,6 +2107,37 @@ 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) +{ + HUIANODE node; + LONG idx[2]; + HRESULT hr; + VARIANT v; + + *iface = NULL; + + VariantInit(&v); + idx[0] = idx[1] = 0; + hr = SafeArrayGetElement(req_data, idx, &v); + if (FAILED(hr)) + goto exit; + + hr = UiaHUiaNodeFromVariant(&v, &node); + if (FAILED(hr)) + goto exit; + VariantClear(&v); + + hr = create_uia_element(iface, from_cui8, node); + +exit: + if (FAILED(hr)) + WARN("Failed to create element from cache request, hr %#lx\n", hr); + + SysFreeString(tree_struct); + return hr; +} + /* * IUIAutomation interface. */
V2: Check `IUIAutomationElementArray::GetElement` index argument for a negative value.
This merge request was approved by Esme Povirk.