-- v2: uiautomationcore: Implement IUIAutomation::get_RawViewCondition. uiautomationcore: Implement IUIAutomation::get_ControlViewCondition. uiautomationcore: Implement IUIAutomation::CreateOrCondition. uiautomationcore: Implement IUIAutomation::CreateNotCondition. uiautomationcore: Implement IUIAutomation::CreatePropertyCondition. uiautomationcore: Implement IUIAutomation::Create{True,False}Condition.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/Makefile.in | 3 + dlls/uiautomationcore/tests/uia_classes.idl | 34 ++++ dlls/uiautomationcore/tests/uiautomation.c | 80 +++++++++ dlls/uiautomationcore/uia_classes.idl | 15 ++ dlls/uiautomationcore/uia_com_client.c | 169 +++++++++++++++++++- 5 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 dlls/uiautomationcore/tests/uia_classes.idl
diff --git a/dlls/uiautomationcore/tests/Makefile.in b/dlls/uiautomationcore/tests/Makefile.in index 53ed6f6e380..7cb4af9e7ff 100644 --- a/dlls/uiautomationcore/tests/Makefile.in +++ b/dlls/uiautomationcore/tests/Makefile.in @@ -3,3 +3,6 @@ IMPORTS = uiautomationcore user32 ole32 oleaut32 oleacc
C_SRCS = \ uiautomation.c + +IDL_SRCS = \ + uia_classes.idl diff --git a/dlls/uiautomationcore/tests/uia_classes.idl b/dlls/uiautomationcore/tests/uia_classes.idl new file mode 100644 index 00000000000..257ca12cf0c --- /dev/null +++ b/dlls/uiautomationcore/tests/uia_classes.idl @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Connor McAdams for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "oaidl.idl"; + +/* + * Undocumented interface used by UI Automation to get the struct UiaCondition + * from an IUIAutomationCondition interface. + */ +[ + local, + object, + uuid(554a6cee-b482-4e17-9a13-7afb8b0d27e1), + pointer_default(unique), +] +interface IUIAutomationConditionInternal : IUnknown +{ + HRESULT get_condition_struct([out, retval]struct UiaCondition **ret_val); +} diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b33a09d4f11..ca2529d87af 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -25,6 +25,7 @@ #include "uiautomation.h" #include "ocidl.h" #include "wine/iaccessible2.h" +#include "uia_classes.h"
#include "wine/test.h"
@@ -10089,6 +10090,84 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface) SafeArrayDestroy(sa); }
+static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) +{ + IUIAutomationConditionInternal *internal_cond; + IUIAutomationBoolCondition *bool_cond; + struct UiaCondition *cond_struct; + IUIAutomationCondition *cond; + BOOL tmp_b; + HRESULT hr; + + hr = IUIAutomation_CreateTrueCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + internal_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationConditionInternal, (void **)&internal_cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!internal_cond, "internal_cond == NULL\n"); + + cond_struct = NULL; + hr = IUIAutomationConditionInternal_get_condition_struct(internal_cond, &cond_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond_struct, "cond_struct == NULL\n"); + ok(cond_struct->ConditionType == ConditionType_True, "Unexpected ConditionType %#x.\n", cond_struct->ConditionType); + IUIAutomationConditionInternal_Release(internal_cond); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationBoolCondition, (void **)&bool_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!bool_cond, "bool_cond == NULL\n"); + + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + tmp_b = FALSE; + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, &tmp_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(tmp_b == TRUE, "tmp_b != TRUE\n"); + IUIAutomationBoolCondition_Release(bool_cond); + + hr = IUIAutomation_CreateFalseCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_CreateFalseCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + internal_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationConditionInternal, (void **)&internal_cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!internal_cond, "internal_cond == NULL\n"); + + cond_struct = NULL; + hr = IUIAutomationConditionInternal_get_condition_struct(internal_cond, &cond_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond_struct, "cond_struct == NULL\n"); + ok(cond_struct->ConditionType == ConditionType_False, "Unexpected ConditionType %#x.\n", cond_struct->ConditionType); + IUIAutomationConditionInternal_Release(internal_cond); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationBoolCondition, (void **)&bool_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!bool_cond, "bool_cond == NULL\n"); + + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + tmp_b = TRUE; + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, &tmp_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(tmp_b == FALSE, "tmp_b != FALSE\n"); + IUIAutomationBoolCondition_Release(bool_cond); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -10187,6 +10266,7 @@ static void test_CUIAutomation(void) IUnknown_Release(unk1); IUnknown_Release(unk2);
+ test_CUIAutomation_condition_ifaces(uia_iface); test_CUIAutomation_value_conversion(uia_iface); test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 7272a09bfd6..21dd0739388 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -45,6 +45,21 @@ struct uia_control_type_info { int control_type_id; };
+/* + * Undocumented interface used by UI Automation to get the struct UiaCondition + * from an IUIAutomationCondition interface. + */ +[ + local, + object, + uuid(554a6cee-b482-4e17-9a13-7afb8b0d27e1), + pointer_default(unique), +] +interface IUIAutomationConditionInternal : IUnknown +{ + HRESULT get_condition_struct([out, retval]struct UiaCondition **ret_val); +} + [ version(1.0), uuid(8a9ca8eb-856b-43d9-abd7-4a590054064f), diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index f8fdfaa6505..71c356305e4 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -23,6 +23,165 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+/* + * Undocumented COM interface shared by all IUIAutomationCondition interfaces, + * used to get struct UiaCondition. + */ +struct uia_condition_internal { + IUIAutomationConditionInternal IUIAutomationConditionInternal_iface; + IUIAutomationCondition *condition; + + struct UiaCondition *condition_struct; +}; + +static inline struct uia_condition_internal *impl_from_IUIAutomationConditionInternal(IUIAutomationConditionInternal *iface) +{ + return CONTAINING_RECORD(iface, struct uia_condition_internal, IUIAutomationConditionInternal_iface); +} + +static HRESULT WINAPI uia_condition_internal_QueryInterface(IUIAutomationConditionInternal *iface, REFIID riid, void **ppv) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_QueryInterface(condition_internal->condition, riid, ppv); +} + +static ULONG WINAPI uia_condition_internal_AddRef(IUIAutomationConditionInternal *iface) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_AddRef(condition_internal->condition); +} + +static ULONG WINAPI uia_condition_internal_Release(IUIAutomationConditionInternal *iface) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_Release(condition_internal->condition); +} + +static HRESULT WINAPI uia_condition_internal_get_condition_struct(IUIAutomationConditionInternal *iface, + struct UiaCondition **ret_val) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + + TRACE("%p, %p\n", iface, ret_val); + + *ret_val = condition_internal->condition_struct; + + return S_OK; +} + +static IUIAutomationConditionInternalVtbl uia_condition_internal_vtbl = { + uia_condition_internal_QueryInterface, + uia_condition_internal_AddRef, + uia_condition_internal_Release, + uia_condition_internal_get_condition_struct, +}; + +static void uia_condition_internal_init(struct uia_condition_internal *internal, struct UiaCondition *condition_struct, + IUIAutomationCondition *condition) +{ + internal->IUIAutomationConditionInternal_iface.lpVtbl = &uia_condition_internal_vtbl; + internal->condition_struct = condition_struct; + internal->condition = condition; +} + +/* + * IUIAutomationBoolCondition interface. + */ +struct uia_bool_condition { + IUIAutomationBoolCondition IUIAutomationBoolCondition_iface; + LONG ref; + + struct UiaCondition condition; + struct uia_condition_internal condition_internal; +}; + +static inline struct uia_bool_condition *impl_from_IUIAutomationBoolCondition(IUIAutomationBoolCondition *iface) +{ + return CONTAINING_RECORD(iface, struct uia_bool_condition, IUIAutomationBoolCondition_iface); +} + +static HRESULT WINAPI uia_bool_condition_QueryInterface(IUIAutomationBoolCondition *iface, REFIID riid, void **ppv) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationBoolCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else if (IsEqualIID(riid, &IID_IUIAutomationConditionInternal)) + *ppv = &uia_bool_condition->condition_internal.IUIAutomationConditionInternal_iface; + else + return E_NOINTERFACE; + + IUIAutomationBoolCondition_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_bool_condition_AddRef(IUIAutomationBoolCondition *iface) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + ULONG ref = InterlockedIncrement(&uia_bool_condition->ref); + + TRACE("%p, refcount %ld\n", uia_bool_condition, ref); + return ref; +} + +static ULONG WINAPI uia_bool_condition_Release(IUIAutomationBoolCondition *iface) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + ULONG ref = InterlockedDecrement(&uia_bool_condition->ref); + + TRACE("%p, refcount %ld\n", uia_bool_condition, ref); + if (!ref) + heap_free(uia_bool_condition); + return ref; +} + +static HRESULT WINAPI uia_bool_condition_get_BooleanValue(IUIAutomationBoolCondition *iface, BOOL *ret_val) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + + TRACE("%p, %p\n", iface, ret_val); + + if (!ret_val) + return E_POINTER; + + if (uia_bool_condition->condition.ConditionType == ConditionType_True) + *ret_val = TRUE; + else + *ret_val = FALSE; + + return S_OK; +} + +static const IUIAutomationBoolConditionVtbl uia_bool_condition_vtbl = { + uia_bool_condition_QueryInterface, + uia_bool_condition_AddRef, + uia_bool_condition_Release, + uia_bool_condition_get_BooleanValue, +}; + +static HRESULT create_uia_bool_condition_iface(IUIAutomationCondition **out_cond, enum ConditionType cond_type) +{ + struct uia_bool_condition *uia_bool_condition; + + if (!out_cond) + return E_POINTER; + + uia_bool_condition = heap_alloc_zero(sizeof(*uia_bool_condition)); + if (!uia_bool_condition) + return E_OUTOFMEMORY; + + uia_bool_condition->IUIAutomationBoolCondition_iface.lpVtbl = &uia_bool_condition_vtbl; + uia_bool_condition->condition.ConditionType = cond_type; + uia_bool_condition->ref = 1; + uia_condition_internal_init(&uia_bool_condition->condition_internal, &uia_bool_condition->condition, + (IUIAutomationCondition *)&uia_bool_condition->IUIAutomationBoolCondition_iface); + + *out_cond = uia_bool_condition->condition_internal.condition; + return S_OK; +} + /* * IUIAutomationElement interface. */ @@ -1217,14 +1376,16 @@ static HRESULT WINAPI uia_iface_CreateCacheRequest(IUIAutomation6 *iface, IUIAut
static HRESULT WINAPI uia_iface_CreateTrueCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_condition); + + return create_uia_bool_condition_iface(out_condition, ConditionType_True); }
static HRESULT WINAPI uia_iface_CreateFalseCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_condition); + + return create_uia_bool_condition_iface(out_condition, ConditionType_False); }
static HRESULT WINAPI uia_iface_CreatePropertyCondition(IUIAutomation6 *iface, PROPERTYID prop_id, VARIANT val,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 82 +++++++++++ dlls/uiautomationcore/uia_com_client.c | 163 ++++++++++++++++++++- 2 files changed, 243 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ca2529d87af..61bd3006244 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10093,11 +10093,16 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface) static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) { IUIAutomationConditionInternal *internal_cond; + struct UiaPropertyCondition *prop_cond_struct; + IUIAutomationPropertyCondition *prop_cond; + enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; struct UiaCondition *cond_struct; IUIAutomationCondition *cond; + PROPERTYID prop_id; BOOL tmp_b; HRESULT hr; + VARIANT v;
hr = IUIAutomation_CreateTrueCondition(uia_iface, NULL); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); @@ -10166,6 +10171,83 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(tmp_b == FALSE, "tmp_b != FALSE\n"); IUIAutomationBoolCondition_Release(bool_cond); + + /* + * IUIAutomationPropertyCondition tests. + */ + /* Invalid property ID. */ + cond = (void *)0xdeadbeef; + VariantInit(&v); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, 0, v, &cond); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + /* Invalid variant type for property ID. */ + cond = (void *)0xdeadbeef; + VariantInit(&v); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_RuntimeIdPropertyId, v, &cond); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + /* NULL Condition. */ + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = create_i4_safearray(); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_RuntimeIdPropertyId, v, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* Finally, create property condition interface. */ + cond = NULL; + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_RuntimeIdPropertyId, v, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + internal_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationConditionInternal, (void **)&internal_cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!internal_cond, "internal_cond == NULL\n"); + + prop_cond_struct = NULL; + hr = IUIAutomationConditionInternal_get_condition_struct(internal_cond, (struct UiaCondition **)&prop_cond_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond_struct, "prop_cond_struct == NULL\n"); + ok(prop_cond_struct->ConditionType == ConditionType_Property, "Unexpected ConditionType %#x.\n", + prop_cond_struct->ConditionType); + ok(prop_cond_struct->PropertyId == UIA_RuntimeIdPropertyId, "Unexpected PropertyId %d\n", + prop_cond_struct->PropertyId); + ok(V_VT(&prop_cond_struct->Value) == (VT_I4 | VT_ARRAY), "Unexpected vt %d\n", V_VT(&prop_cond_struct->Value)); + ok(prop_cond_struct->Flags == PropertyConditionFlags_None, "Unexpected Flags %#x\n", prop_cond_struct->Flags); + IUIAutomationConditionInternal_Release(internal_cond); + + 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, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationPropertyCondition_get_PropertyId(prop_cond, &prop_id); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(prop_id == UIA_RuntimeIdPropertyId, "Unexpected prop_id %d.\n", prop_id); + + hr = IUIAutomationPropertyCondition_get_PropertyValue(prop_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + VariantClear(&v); + hr = IUIAutomationPropertyCondition_get_PropertyValue(prop_cond, &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)); + ok(!!V_ARRAY(&v), "V_ARRAY(&v) == NULL\n"); + VariantClear(&v); + + hr = IUIAutomationPropertyCondition_get_PropertyConditionFlags(prop_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + 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); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 71c356305e4..96ec9c60d6f 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -84,6 +84,164 @@ static void uia_condition_internal_init(struct uia_condition_internal *internal, internal->condition = condition; }
+/* + * IUIAutomationPropertyCondition interface. + */ +struct uia_property_condition { + IUIAutomationPropertyCondition IUIAutomationPropertyCondition_iface; + LONG ref; + + struct UiaPropertyCondition condition; + struct uia_condition_internal condition_internal; +}; + +static inline struct uia_property_condition *impl_from_IUIAutomationPropertyCondition(IUIAutomationPropertyCondition *iface) +{ + return CONTAINING_RECORD(iface, struct uia_property_condition, IUIAutomationPropertyCondition_iface); +} + +static HRESULT WINAPI uia_property_condition_QueryInterface(IUIAutomationPropertyCondition *iface, REFIID riid, void **ppv) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationPropertyCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else if (IsEqualIID(riid, &IID_IUIAutomationConditionInternal)) + *ppv = &uia_property_condition->condition_internal.IUIAutomationConditionInternal_iface; + else + return E_NOINTERFACE; + + IUIAutomationPropertyCondition_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_property_condition_AddRef(IUIAutomationPropertyCondition *iface) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + ULONG ref = InterlockedIncrement(&uia_property_condition->ref); + + TRACE("%p, refcount %ld\n", uia_property_condition, ref); + return ref; +} + +static ULONG WINAPI uia_property_condition_Release(IUIAutomationPropertyCondition *iface) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + ULONG ref = InterlockedDecrement(&uia_property_condition->ref); + + TRACE("%p, refcount %ld\n", uia_property_condition, ref); + + if (!ref) + { + VariantClear(&uia_property_condition->condition.Value); + heap_free(uia_property_condition); + } + + return ref; +} + +static HRESULT WINAPI uia_property_condition_get_PropertyId(IUIAutomationPropertyCondition *iface, PROPERTYID *prop_id) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + + TRACE("%p, %p\n", iface, prop_id); + + if (!prop_id) + return E_POINTER; + + *prop_id = uia_property_condition->condition.PropertyId; + + return S_OK; +} + +static HRESULT WINAPI uia_property_condition_get_PropertyValue(IUIAutomationPropertyCondition *iface, VARIANT *val) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + + TRACE("%p, %p\n", iface, val); + + if (!val) + return E_POINTER; + + VariantCopy(val, &uia_property_condition->condition.Value); + + return S_OK; +} + +static HRESULT WINAPI uia_property_condition_get_PropertyConditionFlags(IUIAutomationPropertyCondition *iface, + enum PropertyConditionFlags *flags) +{ + struct uia_property_condition *uia_property_condition = impl_from_IUIAutomationPropertyCondition(iface); + + TRACE("%p, %p\n", iface, flags); + + if (!flags) + return E_POINTER; + + *flags = uia_property_condition->condition.Flags; + + return S_OK; +} + +static const IUIAutomationPropertyConditionVtbl uia_property_condition_vtbl = { + uia_property_condition_QueryInterface, + uia_property_condition_AddRef, + uia_property_condition_Release, + uia_property_condition_get_PropertyId, + uia_property_condition_get_PropertyValue, + uia_property_condition_get_PropertyConditionFlags, +}; + +static HRESULT create_uia_property_condition_iface(IUIAutomationCondition **out_cond, PROPERTYID prop_id, VARIANT val, + enum PropertyConditionFlags prop_flags) +{ + const struct uia_prop_info *prop_info = uia_prop_info_from_id(prop_id); + struct uia_property_condition *uia_property_condition; + + if (!out_cond) + return E_POINTER; + + *out_cond = NULL; + if (!prop_info) + return E_INVALIDARG; + + switch (prop_info->type) + { + case UIAutomationType_Bool: + if (V_VT(&val) != VT_BOOL) + return E_INVALIDARG; + break; + + case UIAutomationType_IntArray: + if (V_VT(&val) != (VT_I4 | VT_ARRAY)) + return E_INVALIDARG; + break; + + default: + FIXME("Property condition evaluation for property type %#x unimplemented\n", prop_info->type); + return E_NOTIMPL; + } + + uia_property_condition = heap_alloc_zero(sizeof(*uia_property_condition)); + if (!uia_property_condition) + return E_OUTOFMEMORY; + + uia_property_condition->IUIAutomationPropertyCondition_iface.lpVtbl = &uia_property_condition_vtbl; + uia_property_condition->condition.ConditionType = ConditionType_Property; + uia_property_condition->condition.PropertyId = prop_id; + VariantCopy(&uia_property_condition->condition.Value, &val); + uia_property_condition->condition.Flags = prop_flags; + uia_property_condition->ref = 1; + uia_condition_internal_init(&uia_property_condition->condition_internal, + (struct UiaCondition *)&uia_property_condition->condition, + (IUIAutomationCondition *)&uia_property_condition->IUIAutomationPropertyCondition_iface); + + *out_cond = uia_property_condition->condition_internal.condition; + return S_OK; +} + /* * IUIAutomationBoolCondition interface. */ @@ -1391,8 +1549,9 @@ static HRESULT WINAPI uia_iface_CreateFalseCondition(IUIAutomation6 *iface, IUIA static HRESULT WINAPI uia_iface_CreatePropertyCondition(IUIAutomation6 *iface, PROPERTYID prop_id, VARIANT val, IUIAutomationCondition **out_condition) { - FIXME("%p, %d, %s, %p: stub\n", iface, prop_id, debugstr_variant(&val), out_condition); - return E_NOTIMPL; + TRACE("%p, %d, %s, %p\n", iface, prop_id, debugstr_variant(&val), out_condition); + + return create_uia_property_condition_iface(out_condition, prop_id, val, PropertyConditionFlags_None); }
static HRESULT WINAPI uia_iface_CreatePropertyConditionEx(IUIAutomation6 *iface, PROPERTYID prop_id, VARIANT val,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 144 ++++++++++++++++++++- dlls/uiautomationcore/uia_com_client.c | 127 +++++++++++++++++- 2 files changed, 268 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 61bd3006244..93929087f2b 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -106,6 +106,7 @@ DEFINE_EXPECT(Accessible_child2_get_accName); DEFINE_EXPECT(Accessible_child2_get_accRole); DEFINE_EXPECT(Accessible_child2_get_accState); DEFINE_EXPECT(Accessible_child2_accLocation); +DEFINE_EXPECT(ConditionInternal_get_condition_struct);
static BOOL check_variant_i4(VARIANT *v, int val) { @@ -137,6 +138,64 @@ static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) return cmp; }
+struct ConditionInternal +{ + IUIAutomationConditionInternal IUIAutomationConditionInternal_iface; + LONG ref; + + struct UiaCondition condition; + BOOL return_cond_iface; +}; + +static inline struct ConditionInternal *impl_from_ConditionInternal(IUIAutomationConditionInternal *iface) +{ + return CONTAINING_RECORD(iface, struct ConditionInternal, IUIAutomationConditionInternal_iface); +} + +static HRESULT WINAPI ConditionInternal_QueryInterface(IUIAutomationConditionInternal *iface, REFIID riid, void **ppv) +{ + struct ConditionInternal *This = impl_from_ConditionInternal(iface); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + (This->return_cond_iface && IsEqualIID(riid, &IID_IUIAutomationConditionInternal))) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationConditionInternal_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI ConditionInternal_AddRef(IUIAutomationConditionInternal *iface) +{ + struct ConditionInternal *This = impl_from_ConditionInternal(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI ConditionInternal_Release(IUIAutomationConditionInternal *iface) +{ + struct ConditionInternal *This = impl_from_ConditionInternal(iface); + return InterlockedDecrement(&This->ref); +} + +static HRESULT WINAPI ConditionInternal_get_condition_struct(IUIAutomationConditionInternal *iface, struct UiaCondition **val) +{ + struct ConditionInternal *This = impl_from_ConditionInternal(iface); + + CHECK_EXPECT(ConditionInternal_get_condition_struct); + + *val = &This->condition; + return S_OK; +} + +static IUIAutomationConditionInternalVtbl ConditionInternalVtbl = { + ConditionInternal_QueryInterface, + ConditionInternal_AddRef, + ConditionInternal_Release, + ConditionInternal_get_condition_struct, +}; + static struct Accessible { IAccessible IAccessible_iface; @@ -10092,17 +10151,20 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface)
static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) { + struct ConditionInternal ConditionInternal = { { &ConditionInternalVtbl }, 1 }; IUIAutomationConditionInternal *internal_cond; struct UiaPropertyCondition *prop_cond_struct; IUIAutomationPropertyCondition *prop_cond; enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; + IUIAutomationNotCondition *not_cond; struct UiaCondition *cond_struct; IUIAutomationCondition *cond; PROPERTYID prop_id; BOOL tmp_b; HRESULT hr; VARIANT v; + ULONG ref;
hr = IUIAutomation_CreateTrueCondition(uia_iface, NULL); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); @@ -10247,7 +10309,87 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) 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 tests. + */ + cond = (void *)0xdeadbeef; + ConditionInternal.return_cond_iface = FALSE; + + /* + * Interface doesn't return an IUIAutomationConditionInternal interface + * from QI. + */ + hr = IUIAutomation_CreateNotCondition(uia_iface, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, &cond); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + /* + * We return IUIAutomationConditionInternal, treated as a valid + * IUIAutomationCondition interface. + */ + cond = NULL; + ConditionInternal.return_cond_iface = TRUE; + SET_EXPECT(ConditionInternal_get_condition_struct); + hr = IUIAutomation_CreateNotCondition(uia_iface, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + CHECK_CALLED(ConditionInternal_get_condition_struct); + ok(ConditionInternal.ref == 2, "Unexpected ref %ld", ConditionInternal.ref); + + 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(iface_cmp((IUnknown *)cond, (IUnknown *)&ConditionInternal.IUIAutomationConditionInternal_iface), + "cond != ConditionInternal\n"); + IUIAutomationCondition_Release(cond); + + IUIAutomationNotCondition_Release(not_cond); + ok(ConditionInternal.ref == 1, "Unexpected ref %ld", ConditionInternal.ref); + + /* NULL input argument tests. */ + cond = (void *)0xdeadbeef; + hr = IUIAutomation_CreateNotCondition(uia_iface, NULL, &cond); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + hr = IUIAutomation_CreateNotCondition(uia_iface, (IUIAutomationCondition *)prop_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* + * Now test with an IUIAutomationCondition interface returned by UI + * Automation. + */ + cond = NULL; + hr = IUIAutomation_CreateNotCondition(uia_iface, (IUIAutomationCondition *)prop_cond, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + /* IUIAutomationNotCondition holds a reference to the passed in condition. */ + ref = IUIAutomationPropertyCondition_Release(prop_cond); + ok(ref == 1, "Unexpected ref %ld\n", ref); + + 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"); + + hr = IUIAutomationNotCondition_GetChild(not_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomationNotCondition_GetChild(not_cond, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(iface_cmp((IUnknown *)cond, (IUnknown *)prop_cond), "cond != prop_cond\n"); + IUIAutomationCondition_Release(cond); + + IUIAutomationNotCondition_Release(not_cond); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 96ec9c60d6f..c063b7f796a 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -84,6 +84,128 @@ static void uia_condition_internal_init(struct uia_condition_internal *internal, internal->condition = condition; }
+static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condition, struct UiaCondition **cond_struct) +{ + IUIAutomationConditionInternal *cond_internal; + HRESULT hr; + + *cond_struct = NULL; + if (!condition) + return E_POINTER; + + hr = IUIAutomationCondition_QueryInterface(condition, &IID_IUIAutomationConditionInternal, (void **)&cond_internal); + if (FAILED(hr)) + return E_FAIL; + + hr = IUIAutomationConditionInternal_get_condition_struct(cond_internal, cond_struct); + IUIAutomationConditionInternal_Release(cond_internal); + + return hr; +} + +/* + * IUIAutomationNotCondition interface. + */ +struct uia_not_condition { + IUIAutomationNotCondition IUIAutomationNotCondition_iface; + LONG ref; + + IUIAutomationCondition *child_iface; + struct UiaNotCondition condition; +}; + +static inline struct uia_not_condition *impl_from_IUIAutomationNotCondition(IUIAutomationNotCondition *iface) +{ + return CONTAINING_RECORD(iface, struct uia_not_condition, IUIAutomationNotCondition_iface); +} + +static HRESULT WINAPI uia_not_condition_QueryInterface(IUIAutomationNotCondition *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationNotCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationNotCondition_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_not_condition_AddRef(IUIAutomationNotCondition *iface) +{ + struct uia_not_condition *uia_not_condition = impl_from_IUIAutomationNotCondition(iface); + ULONG ref = InterlockedIncrement(&uia_not_condition->ref); + + TRACE("%p, refcount %ld\n", uia_not_condition, ref); + return ref; +} + +static ULONG WINAPI uia_not_condition_Release(IUIAutomationNotCondition *iface) +{ + struct uia_not_condition *uia_not_condition = impl_from_IUIAutomationNotCondition(iface); + ULONG ref = InterlockedDecrement(&uia_not_condition->ref); + + TRACE("%p, refcount %ld\n", uia_not_condition, ref); + if (!ref) + { + IUIAutomationCondition_Release(uia_not_condition->child_iface); + heap_free(uia_not_condition); + } + return ref; +} + +static HRESULT WINAPI uia_not_condition_GetChild(IUIAutomationNotCondition *iface, IUIAutomationCondition **child) +{ + struct uia_not_condition *uia_not_condition = impl_from_IUIAutomationNotCondition(iface); + + TRACE("%p, %p\n", iface, child); + + if (!child) + return E_POINTER; + + IUIAutomationCondition_AddRef(uia_not_condition->child_iface); + *child = uia_not_condition->child_iface; + + return S_OK; +} + +static const IUIAutomationNotConditionVtbl uia_not_condition_vtbl = { + uia_not_condition_QueryInterface, + uia_not_condition_AddRef, + uia_not_condition_Release, + uia_not_condition_GetChild, +}; + +static HRESULT create_uia_not_condition_iface(IUIAutomationCondition **out_cond, IUIAutomationCondition *in_cond) +{ + struct uia_not_condition *uia_not_condition; + struct UiaCondition *cond_struct; + HRESULT hr; + + if (!out_cond) + return E_POINTER; + + *out_cond = NULL; + hr = get_uia_condition_struct_from_iface(in_cond, &cond_struct); + if (FAILED(hr)) + return hr; + + uia_not_condition = heap_alloc_zero(sizeof(*uia_not_condition)); + if (!uia_not_condition) + return E_OUTOFMEMORY; + + uia_not_condition->IUIAutomationNotCondition_iface.lpVtbl = &uia_not_condition_vtbl; + uia_not_condition->condition.ConditionType = ConditionType_Not; + uia_not_condition->condition.pConditions = cond_struct; + uia_not_condition->ref = 1; + uia_not_condition->child_iface = in_cond; + IUIAutomationCondition_AddRef(in_cond); + + *out_cond = (IUIAutomationCondition *)&uia_not_condition->IUIAutomationNotCondition_iface; + return S_OK; +} + /* * IUIAutomationPropertyCondition interface. */ @@ -1606,8 +1728,9 @@ static HRESULT WINAPI uia_iface_CreateOrConditionFromNativeArray(IUIAutomation6 static HRESULT WINAPI uia_iface_CreateNotCondition(IUIAutomation6 *iface, IUIAutomationCondition *cond, IUIAutomationCondition **out_condition) { - FIXME("%p, %p, %p: stub\n", iface, cond, out_condition); - return E_NOTIMPL; + TRACE("%p, %p, %p\n", iface, cond, out_condition); + + return create_uia_not_condition_iface(out_condition, cond); }
static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 99 ++++++++++- dlls/uiautomationcore/uia_com_client.c | 194 ++++++++++++++++++++- 2 files changed, 290 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 93929087f2b..0d30ce26f88 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10155,12 +10155,14 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationConditionInternal *internal_cond; struct UiaPropertyCondition *prop_cond_struct; IUIAutomationPropertyCondition *prop_cond; + IUIAutomationCondition *cond, **cond_arr; enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; IUIAutomationNotCondition *not_cond; + IUIAutomationOrCondition *or_cond; struct UiaCondition *cond_struct; - IUIAutomationCondition *cond; PROPERTYID prop_id; + int child_count; BOOL tmp_b; HRESULT hr; VARIANT v; @@ -10390,6 +10392,101 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationCondition_Release(cond);
IUIAutomationNotCondition_Release(not_cond); + + /* + * IUIAutomationOrCondition tests. + */ + cond = NULL; + VariantInit(&v); + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_IsControlElementPropertyId, v, &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"); + + /* NULL input argument tests. */ + hr = IUIAutomation_CreateOrCondition(uia_iface, (IUIAutomationCondition *)prop_cond, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = (void *)0xdeadbeef; + hr = IUIAutomation_CreateOrCondition(uia_iface, NULL, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, &cond); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + cond = (void *)0xdeadbeef; + hr = IUIAutomation_CreateOrCondition(uia_iface, (IUIAutomationCondition *)prop_cond, NULL, &cond); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + /* One of the IUIAutomationCondition interfaces are invalid. */ + cond = (void *)0xdeadbeef; + ConditionInternal.return_cond_iface = FALSE; + hr = IUIAutomation_CreateOrCondition(uia_iface, (IUIAutomationCondition *)prop_cond, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, &cond); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok(!cond, "cond != NULL\n"); + + cond = NULL; + ConditionInternal.return_cond_iface = TRUE; + SET_EXPECT(ConditionInternal_get_condition_struct); + hr = IUIAutomation_CreateOrCondition(uia_iface, (IUIAutomationCondition *)prop_cond, + (IUIAutomationCondition *)&ConditionInternal.IUIAutomationConditionInternal_iface, &cond); + ok(ConditionInternal.ref == 2, "Unexpected ref %ld", ConditionInternal.ref); + CHECK_CALLED(ConditionInternal_get_condition_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + or_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationOrCondition, (void **)&or_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!or_cond, "or_cond == NULL\n"); + + ref = IUIAutomationPropertyCondition_Release(prop_cond); + ok(ref == 1, "Unexpected ref %ld\n", ref); + + hr = IUIAutomationOrCondition_get_ChildCount(or_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + child_count = 0; + hr = IUIAutomationOrCondition_get_ChildCount(or_cond, &child_count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(child_count == 2, "Unexpected child_count %d.\n", child_count); + + child_count = 10; + hr = IUIAutomationOrCondition_GetChildrenAsNativeArray(or_cond, NULL, &child_count); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(child_count == 10, "Unexpected child_count %d.\n", child_count); + + cond_arr = (void *)0xdeadbeef; + hr = IUIAutomationOrCondition_GetChildrenAsNativeArray(or_cond, &cond_arr, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!cond_arr, "cond_arr != NULL\n"); + + child_count = 0; + cond_arr = NULL; + hr = IUIAutomationOrCondition_GetChildrenAsNativeArray(or_cond, &cond_arr, &child_count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(child_count == 2, "Unexpected child_count %d.\n", child_count); + ok(!!cond_arr, "cond_arr == NULL\n"); + + ok(iface_cmp((IUnknown *)cond_arr[0], (IUnknown *)prop_cond), "cond_arr[0] != prop_cond\n"); + IUIAutomationCondition_Release(cond_arr[0]); + + ok(iface_cmp((IUnknown *)cond_arr[1], (IUnknown *)&ConditionInternal.IUIAutomationConditionInternal_iface), + "cond_arr[1] != ConditionInternal\n"); + IUIAutomationCondition_Release(cond_arr[1]); + + CoTaskMemFree(cond_arr); + IUIAutomationOrCondition_Release(or_cond); + ok(ConditionInternal.ref == 1, "Unexpected ref %ld", ConditionInternal.ref); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index c063b7f796a..5bca396ce6b 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -103,6 +103,193 @@ static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condi return hr; }
+/* + * IUIAutomationOrCondition interface. + */ +struct uia_or_condition { + IUIAutomationOrCondition IUIAutomationOrCondition_iface; + LONG ref; + + IUIAutomationCondition **child_ifaces; + int child_count; + + struct UiaAndOrCondition condition; + struct uia_condition_internal condition_internal; +}; + +static inline struct uia_or_condition *impl_from_IUIAutomationOrCondition(IUIAutomationOrCondition *iface) +{ + return CONTAINING_RECORD(iface, struct uia_or_condition, IUIAutomationOrCondition_iface); +} + +static HRESULT WINAPI uia_or_condition_QueryInterface(IUIAutomationOrCondition *iface, REFIID riid, void **ppv) +{ + struct uia_or_condition *uia_or_condition = impl_from_IUIAutomationOrCondition(iface); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationOrCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else if (IsEqualIID(riid, &IID_IUIAutomationConditionInternal)) + *ppv = &uia_or_condition->condition_internal.IUIAutomationConditionInternal_iface; + else + return E_NOINTERFACE; + + IUIAutomationOrCondition_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_or_condition_AddRef(IUIAutomationOrCondition *iface) +{ + struct uia_or_condition *uia_or_condition = impl_from_IUIAutomationOrCondition(iface); + ULONG ref = InterlockedIncrement(&uia_or_condition->ref); + + TRACE("%p, refcount %ld\n", uia_or_condition, ref); + return ref; +} + +static ULONG WINAPI uia_or_condition_Release(IUIAutomationOrCondition *iface) +{ + struct uia_or_condition *uia_or_condition = impl_from_IUIAutomationOrCondition(iface); + ULONG ref = InterlockedDecrement(&uia_or_condition->ref); + + TRACE("%p, refcount %ld\n", uia_or_condition, ref); + + if (!ref) + { + if (uia_or_condition->child_ifaces) + { + int i; + + for (i = 0; i < uia_or_condition->child_count; i++) + { + if (uia_or_condition->child_ifaces[i]) + IUIAutomationCondition_Release(uia_or_condition->child_ifaces[i]); + } + } + + heap_free(uia_or_condition->child_ifaces); + heap_free(uia_or_condition->condition.ppConditions); + heap_free(uia_or_condition); + } + + return ref; +} + +static HRESULT WINAPI uia_or_condition_get_ChildCount(IUIAutomationOrCondition *iface, int *child_count) +{ + struct uia_or_condition *uia_or_condition = impl_from_IUIAutomationOrCondition(iface); + + TRACE("%p, %p\n", iface, child_count); + + if (!child_count) + return E_POINTER; + + *child_count = uia_or_condition->child_count; + + return S_OK; +} + +static HRESULT WINAPI uia_or_condition_GetChildrenAsNativeArray(IUIAutomationOrCondition *iface, + IUIAutomationCondition ***out_children, int *out_children_count) +{ + struct uia_or_condition *uia_or_condition = impl_from_IUIAutomationOrCondition(iface); + IUIAutomationCondition **children; + int i; + + TRACE("%p, %p, %p\n", iface, out_children, out_children_count); + + if (!out_children) + return E_POINTER; + + *out_children = NULL; + + if (!out_children_count) + return E_POINTER; + + if (!(children = CoTaskMemAlloc(uia_or_condition->child_count * sizeof(*children)))) + return E_OUTOFMEMORY; + + for (i = 0; i < uia_or_condition->child_count; i++) + { + children[i] = uia_or_condition->child_ifaces[i]; + IUIAutomationCondition_AddRef(uia_or_condition->child_ifaces[i]); + } + + *out_children = children; + *out_children_count = uia_or_condition->child_count; + + return S_OK; +} + +static HRESULT WINAPI uia_or_condition_GetChildren(IUIAutomationOrCondition *iface, SAFEARRAY **out_children) +{ + FIXME("%p, %p: stub\n", iface, out_children); + return E_NOTIMPL; +} + +static const IUIAutomationOrConditionVtbl uia_or_condition_vtbl = { + uia_or_condition_QueryInterface, + uia_or_condition_AddRef, + uia_or_condition_Release, + uia_or_condition_get_ChildCount, + uia_or_condition_GetChildrenAsNativeArray, + uia_or_condition_GetChildren, +}; + +static HRESULT create_uia_or_condition_iface(IUIAutomationCondition **out_cond, IUIAutomationCondition **in_conds, + int in_cond_count) +{ + struct uia_or_condition *uia_or_condition; + int i; + + if (!out_cond) + return E_POINTER; + + *out_cond = NULL; + + uia_or_condition = heap_alloc_zero(sizeof(*uia_or_condition)); + if (!uia_or_condition) + return E_OUTOFMEMORY; + + uia_or_condition->IUIAutomationOrCondition_iface.lpVtbl = &uia_or_condition_vtbl; + uia_or_condition->ref = 1; + + uia_or_condition->child_ifaces = heap_alloc_zero(sizeof(*in_conds) * in_cond_count); + if (!uia_or_condition->child_ifaces) + { + IUIAutomationOrCondition_Release(&uia_or_condition->IUIAutomationOrCondition_iface); + return E_OUTOFMEMORY; + } + + uia_or_condition->condition.ppConditions = heap_alloc_zero(sizeof(*uia_or_condition->condition.ppConditions) * in_cond_count); + if (!uia_or_condition->condition.ppConditions) + { + IUIAutomationOrCondition_Release(&uia_or_condition->IUIAutomationOrCondition_iface); + return E_OUTOFMEMORY; + } + + uia_or_condition->condition.ConditionType = ConditionType_Or; + uia_or_condition->child_count = uia_or_condition->condition.cConditions = in_cond_count; + for (i = 0; i < in_cond_count; i++) + { + HRESULT hr; + + hr = get_uia_condition_struct_from_iface(in_conds[i], &uia_or_condition->condition.ppConditions[i]); + if (FAILED(hr)) + { + IUIAutomationOrCondition_Release(&uia_or_condition->IUIAutomationOrCondition_iface); + return hr; + } + + uia_or_condition->child_ifaces[i] = in_conds[i]; + IUIAutomationCondition_AddRef(in_conds[i]); + } + + *out_cond = (IUIAutomationCondition *)&uia_or_condition->IUIAutomationOrCondition_iface; + return S_OK; +} + /* * IUIAutomationNotCondition interface. */ @@ -1707,8 +1894,11 @@ static HRESULT WINAPI uia_iface_CreateAndConditionFromNativeArray(IUIAutomation6 static HRESULT WINAPI uia_iface_CreateOrCondition(IUIAutomation6 *iface, IUIAutomationCondition *cond1, IUIAutomationCondition *cond2, IUIAutomationCondition **out_condition) { - FIXME("%p, %p, %p, %p: stub\n", iface, cond1, cond2, out_condition); - return E_NOTIMPL; + IUIAutomationCondition *cond_arr[2] = { cond1, cond2 }; + + TRACE("%p, %p, %p, %p\n", iface, cond1, cond2, out_condition); + + return create_uia_or_condition_iface(out_condition, cond_arr, ARRAY_SIZE(cond_arr)); }
static HRESULT WINAPI uia_iface_CreateOrConditionFromArray(IUIAutomation6 *iface, SAFEARRAY *conds,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 44 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 30 ++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 0d30ce26f88..ec454690cd8 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10487,6 +10487,50 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) CoTaskMemFree(cond_arr); IUIAutomationOrCondition_Release(or_cond); ok(ConditionInternal.ref == 1, "Unexpected ref %ld", ConditionInternal.ref); + + /* + * Condition used to get the control TreeView. Equivalent to: + * if (!(UIA_IsControlElementPropertyId == VARIANT_FALSE)) + */ + hr = IUIAutomation_get_ControlViewCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_get_ControlViewCondition(uia_iface, &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); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 5bca396ce6b..222b26f7398 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1825,8 +1825,34 @@ static HRESULT WINAPI uia_iface_get_RawViewCondition(IUIAutomation6 *iface, IUIA
static HRESULT WINAPI uia_iface_get_ControlViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + 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; }
static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 23 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 5 +++-- 2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ec454690cd8..beff5463a40 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10531,6 +10531,29 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface)
IUIAutomationPropertyCondition_Release(prop_cond); IUIAutomationNotCondition_Release(not_cond); + + /* + * Condition used to get the raw TreeView. Equivalent to: + * if (1) + */ + hr = IUIAutomation_get_RawViewCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_get_RawViewCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationBoolCondition, (void **)&bool_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!bool_cond, "bool_cond == NULL\n"); + + tmp_b = FALSE; + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, &tmp_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(tmp_b == TRUE, "tmp_b != TRUE\n"); + IUIAutomationBoolCondition_Release(bool_cond); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 222b26f7398..c3e51ba7159 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1819,8 +1819,9 @@ static HRESULT WINAPI uia_iface_get_RawViewWalker(IUIAutomation6 *iface, IUIAuto
static HRESULT WINAPI uia_iface_get_RawViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_condition); + + return create_uia_bool_condition_iface(out_condition, ConditionType_True); }
static HRESULT WINAPI uia_iface_get_ControlViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition)
V2:
-Validate input arguments for all methods (make sure pointers aren't NULL, and that the passed in interfaces are valid).
-Make sure to release `IUIAutomationCondition` interfaces held by `IUIAutomationOrCondition` interfaces.
-Don't check for NULL before calling `heap_free()`.
-Instead of checking vtbl's to validate `IUIAutomationCondition` interfaces, make use of an undocumented interface.
Also, looks like I forgot `makedep header` in the new test idl, will push a fix.