From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 41 ++++++++++ dlls/uiautomationcore/uia_com_client.c | 95 +++++++++++++++++++++- 2 files changed, 132 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b33a09d4f11..887ef3ec9a1 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10089,6 +10089,46 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface) SafeArrayDestroy(sa); }
+static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) +{ + IUIAutomationBoolCondition *bool_cond; + IUIAutomationCondition *cond; + BOOL tmp_b; + HRESULT hr; + + cond = NULL; + hr = IUIAutomation_CreateTrueCondition(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); + + cond = NULL; + hr = IUIAutomation_CreateFalseCondition(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 = 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 +10227,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_com_client.c b/dlls/uiautomationcore/uia_com_client.c index f8fdfaa6505..49e6899a1b4 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -23,6 +23,91 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+/* + * IUIAutomationBoolCondition interface. + */ +struct uia_bool_condition { + IUIAutomationBoolCondition IUIAutomationBoolCondition_iface; + LONG ref; + + struct UiaCondition condition; +}; + +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) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationBoolCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = 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 (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; + + 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; + + *out_cond = (IUIAutomationCondition *)&uia_bool_condition->IUIAutomationBoolCondition_iface; + return S_OK; +} + /* * IUIAutomationElement interface. */ @@ -1217,14 +1302,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 | 36 ++++++ dlls/uiautomationcore/uia_com_client.c | 121 ++++++++++++++++++++- 2 files changed, 155 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 887ef3ec9a1..a04801960a7 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10091,10 +10091,14 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface)
static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) { + IUIAutomationPropertyCondition *prop_cond; + enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; IUIAutomationCondition *cond; + PROPERTYID prop_id; BOOL tmp_b; HRESULT hr; + VARIANT v;
cond = NULL; hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); @@ -10127,6 +10131,38 @@ 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. + */ + cond = NULL; + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = create_i4_safearray(); + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_RuntimeIdPropertyId, 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"); + + 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); + + 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, &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 49e6899a1b4..c830a89bc3e 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -23,6 +23,122 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+/* + * IUIAutomationPropertyCondition interface. + */ +struct uia_property_condition { + IUIAutomationPropertyCondition IUIAutomationPropertyCondition_iface; + LONG ref; + + struct UiaPropertyCondition condition; +}; + +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) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationPropertyCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = 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); + + *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); + + 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); + + *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) +{ + struct uia_property_condition *uia_property_condition; + + 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; + + *out_cond = (IUIAutomationCondition *)&uia_property_condition->IUIAutomationPropertyCondition_iface; + return S_OK; +} + /* * IUIAutomationBoolCondition interface. */ @@ -1317,8 +1433,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 | 27 ++++- dlls/uiautomationcore/uia_com_client.c | 128 ++++++++++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a04801960a7..c1a17b88aa5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10094,11 +10094,13 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationPropertyCondition *prop_cond; enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; + IUIAutomationNotCondition *not_cond; IUIAutomationCondition *cond; PROPERTYID prop_id; BOOL tmp_b; HRESULT hr; VARIANT v; + ULONG ref;
cond = NULL; hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); @@ -10162,7 +10164,30 @@ 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 = 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"); + + 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 c830a89bc3e..8640253de59 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -23,6 +23,98 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+static struct UiaCondition *get_uia_condition_struct_from_iface(IUIAutomationCondition *condition_iface); + +/* + * 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); + + 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; + + 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 = get_uia_condition_struct_from_iface(in_cond); + 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. */ @@ -224,6 +316,37 @@ static HRESULT create_uia_bool_condition_iface(IUIAutomationCondition **out_cond return S_OK; }
+static struct UiaCondition *get_uia_condition_struct_from_iface(IUIAutomationCondition *condition_iface) +{ + struct UiaCondition *condition; + + if (condition_iface->lpVtbl == (IUIAutomationConditionVtbl *)&uia_bool_condition_vtbl) + { + IUIAutomationBoolCondition *bool_cond = (IUIAutomationBoolCondition *)condition_iface; + struct uia_bool_condition *cond = impl_from_IUIAutomationBoolCondition(bool_cond); + + condition = &cond->condition; + } + else if (condition_iface->lpVtbl == (IUIAutomationConditionVtbl *)&uia_property_condition_vtbl) + { + IUIAutomationPropertyCondition *prop_cond = (IUIAutomationPropertyCondition *)condition_iface; + struct uia_property_condition *cond = impl_from_IUIAutomationPropertyCondition(prop_cond); + + condition = (struct UiaCondition *)&cond->condition; + } + else if (condition_iface->lpVtbl == (IUIAutomationConditionVtbl *)&uia_not_condition_vtbl) + { + IUIAutomationNotCondition *not_cond = (IUIAutomationNotCondition *)condition_iface; + struct uia_not_condition *cond = impl_from_IUIAutomationNotCondition(not_cond); + + condition = (struct UiaCondition *)&cond->condition; + } + else + condition = NULL; + + return condition; +} + /* * IUIAutomationElement interface. */ @@ -1490,8 +1613,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 | 70 ++++++++- dlls/uiautomationcore/uia_com_client.c | 165 ++++++++++++++++++++- 2 files changed, 231 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index c1a17b88aa5..ecef18a798a 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10091,12 +10091,14 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface)
static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) { - IUIAutomationPropertyCondition *prop_cond; + IUIAutomationPropertyCondition *prop_cond, *prop_cond2; + IUIAutomationCondition *cond, **cond_arr; enum PropertyConditionFlags prop_flags; IUIAutomationBoolCondition *bool_cond; IUIAutomationNotCondition *not_cond; - IUIAutomationCondition *cond; + IUIAutomationOrCondition *or_cond; PROPERTYID prop_id; + int child_count; BOOL tmp_b; HRESULT hr; VARIANT v; @@ -10188,6 +10190,70 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationCondition_Release(cond);
IUIAutomationNotCondition_Release(not_cond); + + /* + * IUIAutomationOrCondition tests. + */ + cond = NULL; + 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"); + + cond = NULL; + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + hr = IUIAutomation_CreatePropertyCondition(uia_iface, UIA_IsContentElementPropertyId, v, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationPropertyCondition, (void **)&prop_cond2); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!prop_cond2, "prop_cond2 == NULL\n"); + + hr = IUIAutomation_CreateOrCondition(uia_iface, (IUIAutomationCondition *)prop_cond, + (IUIAutomationCondition *)prop_cond2, &cond); + 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); + + ref = IUIAutomationPropertyCondition_Release(prop_cond2); + ok(ref == 1, "Unexpected ref %ld\n", ref); + + 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 = 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 *)prop_cond2), "cond_arr[1] != prop_cond2\n"); + IUIAutomationCondition_Release(cond_arr[1]); + + CoTaskMemFree(cond_arr); + IUIAutomationOrCondition_Release(or_cond); }
struct uia_com_classes { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 8640253de59..89cbd7d607c 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -25,6 +25,157 @@ WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
static struct UiaCondition *get_uia_condition_struct_from_iface(IUIAutomationCondition *condition_iface);
+/* + * IUIAutomationOrCondition interface. + */ +struct uia_or_condition { + IUIAutomationOrCondition IUIAutomationOrCondition_iface; + LONG ref; + + IUIAutomationCondition **child_ifaces; + int child_count; + + struct UiaAndOrCondition condition; +}; + +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) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationOrCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = 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) + heap_free(uia_or_condition->child_ifaces); + if (uia_or_condition->condition.ppConditions) + 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); + + *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); + + *out_children = NULL; + 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; + + 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++) + { + uia_or_condition->child_ifaces[i] = in_conds[i]; + uia_or_condition->condition.ppConditions[i] = get_uia_condition_struct_from_iface(in_conds[i]); + IUIAutomationCondition_AddRef(in_conds[i]); + } + + *out_cond = (IUIAutomationCondition *)&uia_or_condition->IUIAutomationOrCondition_iface; + return S_OK; +} + /* * IUIAutomationNotCondition interface. */ @@ -341,6 +492,13 @@ static struct UiaCondition *get_uia_condition_struct_from_iface(IUIAutomationCon
condition = (struct UiaCondition *)&cond->condition; } + else if (condition_iface->lpVtbl == (IUIAutomationConditionVtbl *)&uia_or_condition_vtbl) + { + IUIAutomationOrCondition *or_cond = (IUIAutomationOrCondition *)condition_iface; + struct uia_or_condition *cond = impl_from_IUIAutomationOrCondition(or_cond); + + condition = (struct UiaCondition *)&cond->condition; + } else condition = NULL;
@@ -1592,8 +1750,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 | 41 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 26 ++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ecef18a798a..113ed0612de 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10254,6 +10254,47 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface)
CoTaskMemFree(cond_arr); IUIAutomationOrCondition_Release(or_cond); + + /* + * Condition used to get the control TreeView. Equivalent to: + * if (!(UIA_IsControlElementPropertyId == VARIANT_FALSE)) + */ + 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 89cbd7d607c..bf5ddcba5f1 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1681,8 +1681,30 @@ 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); + + *out_condition = NULL; + + 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 | 20 ++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 113ed0612de..ced4134bc74 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10295,6 +10295,26 @@ 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) + */ + 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 bf5ddcba5f1..63c319cb378 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1675,8 +1675,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)
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_com_client.c:
- 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;
- 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 = get_uia_condition_struct_from_iface(in_cond);
Should in_cond or the resulting UiaCondition* be checked for NULL?
On Wed Mar 1 20:37:46 2023 +0000, Esme Povirk wrote:
Should in_cond or the resulting UiaCondition* be checked for NULL?
Yeah, I guess it would probably be a good idea to add input argument NULL checks and confirm that the interface passed in has a valid `IUIAutomationCondition` interface vtbl.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_com_client.c:
+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)
heap_free(uia_or_condition->child_ifaces);
if (uia_or_condition->condition.ppConditions)
heap_free(uia_or_condition->condition.ppConditions);
heap_free(uia_or_condition);
- }
This should Release the child interfaces, I think.
On Wed Mar 1 20:51:01 2023 +0000, Esme Povirk wrote:
This should Release the child interfaces, I think.
Also, checking for NULL is not necessary before calling heap_free.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_com_client.c:
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);
- *out_condition = NULL;
- V_VT(&v) = VT_BOOL;
- V_BOOL(&v) = VARIANT_FALSE;
v should probably be initialized to 0, or VariantInit should be called first.