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,