-- v3: 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 | 36 +++++ dlls/uiautomationcore/tests/uiautomation.c | 80 +++++++++ dlls/uiautomationcore/uia_classes.idl | 15 ++ dlls/uiautomationcore/uia_com_client.c | 169 +++++++++++++++++++- 5 files changed, 299 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..24addb2b820 --- /dev/null +++ b/dlls/uiautomationcore/tests/uia_classes.idl @@ -0,0 +1,36 @@ +/* + * 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 + */ + +#pragma makedep header + +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)
V3: Add a missing `#pragma makedep header` to the new `uia_classes.idl` file in the tests folder.
I'm not sure if it's OK to use the undocumented interface? While an application developer could, in theory, make a IUIAutomationCondition implementation and find out about it that way, it's not clear that there are any cases of it, and it seems unlikely to me.
On Fri Mar 3 19:27:01 2023 +0000, Esme Povirk wrote:
I'm not sure if it's OK to use the undocumented interface? While an application developer could, in theory, make a IUIAutomationCondition implementation and find out about it that way, it's not clear that there are any cases of it, and it seems unlikely to me.
I guess we don't really have to match this behavior if there's no existing application that does so, but at the same time I also don't think it harms anything either.
It can be done another way if you'd prefer that.
On Fri Mar 3 19:32:28 2023 +0000, Connor McAdams wrote:
I guess we don't really have to match this behavior if there's no existing application that does so, but at the same time I also don't think it harms anything either. It can be done another way if you'd prefer that.
Also, let me know if you'd prefer it to be done in another way, because I'm not sure if this was meant to be a question or a definitive "don't do that". :)
On Fri Mar 3 20:19:58 2023 +0000, Connor McAdams wrote:
Also, let me know if you'd prefer it to be done in another way, because I'm not sure if this was meant to be a question or a definitive "don't do that". :)
It is a question, I'm really unsure.
On Fri Mar 3 21:50:44 2023 +0000, Esme Povirk wrote:
It is a question, I'm really unsure.
Well, given I don't have a strong opinion on keeping this, I'll just redo it without the undocumented interface to avoid any issues.