From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 166 ++++++++++++ dlls/uiautomationcore/uia_com_client.c | 290 +++++++++++++++++++-- 2 files changed, 428 insertions(+), 28 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 66221a84d44..8efe6319154 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10555,6 +10555,171 @@ static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) IUIAutomationBoolCondition_Release(bool_cond); }
+static void test_CUIAutomation_cache_request_iface(IUIAutomation *uia_iface) +{ + IUIAutomationPropertyCondition *prop_cond; + enum PropertyConditionFlags prop_flags; + IUIAutomationCacheRequest *cache_req; + enum AutomationElementMode elem_mode; + IUIAutomationCondition *cond, *cond2; + IUIAutomationNotCondition *not_cond; + enum TreeScope scope; + PROPERTYID prop_id; + HRESULT hr; + VARIANT v; + + hr = IUIAutomation_CreateCacheRequest(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* + * CreateCacheRequest returns an IUIAutomationCacheRequest with the + * default cache request values set. + */ + cache_req = NULL; + hr = IUIAutomation_CreateCacheRequest(uia_iface, &cache_req); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cache_req, "cache_req == NULL\n"); + + /* + * TreeScope tests. + */ + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(scope == TreeScope_Element, "Unexpected scope %#x\n", scope); + + /* Set it to something invalid. */ + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Parent); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Ancestors); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Parent | TreeScope_Element); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Ancestors | TreeScope_Element); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, ~(TreeScope_SubTree | TreeScope_Parent | TreeScope_Ancestors)); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Invalid values don't change anything. */ + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(scope == TreeScope_Element, "Unexpected scope %#x\n", scope); + + /* Now set it to TreeScope_Children. */ + hr = IUIAutomationCacheRequest_put_TreeScope(cache_req, TreeScope_Children); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + scope = 0; + hr = IUIAutomationCacheRequest_get_TreeScope(cache_req, &scope); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(scope == TreeScope_Children, "Unexpected scope %#x\n", scope); + + /* + * TreeFilter tests. + */ + cond = NULL; + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, (IUIAutomationCondition *)&Object); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + + /* Default IUIAutomationCacheRequest has the ControlView condition. */ + cond = NULL; + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationNotCondition, (void **)¬_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!not_cond, "not_cond == NULL\n"); + + cond = NULL; + hr = IUIAutomationNotCondition_GetChild(not_cond, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationPropertyCondition, (void **)&prop_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!prop_cond, "prop_cond == NULL\n"); + + hr = IUIAutomationPropertyCondition_get_PropertyId(prop_cond, &prop_id); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(prop_id == UIA_IsControlElementPropertyId, "Unexpected prop_id %d.\n", prop_id); + + VariantInit(&v); + hr = IUIAutomationPropertyCondition_get_PropertyValue(prop_cond, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + VariantClear(&v); + + hr = IUIAutomationPropertyCondition_get_PropertyConditionFlags(prop_cond, &prop_flags); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(prop_flags == PropertyConditionFlags_None, "Unexpected flags %#x.\n", prop_flags); + + IUIAutomationPropertyCondition_Release(prop_cond); + IUIAutomationNotCondition_Release(not_cond); + + /* Set a new TreeFilter condition. */ + cond = NULL; + hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + hr = IUIAutomationCacheRequest_put_TreeFilter(cache_req, cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_get_TreeFilter(cache_req, &cond2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond2, "cond2 == NULL\n"); + ok(iface_cmp((IUnknown *)cond, (IUnknown *)cond2), "cond != cond2\n"); + IUIAutomationCondition_Release(cond); + IUIAutomationCondition_Release(cond2); + + /* + * AutomationElementMode tests. + */ + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + elem_mode = 0; + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, &elem_mode); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(elem_mode == AutomationElementMode_Full, "Unexpected element mode %#x\n", elem_mode); + + /* Invalid value - maximum is AutomationElementMode_Full, 0x01. */ + hr = IUIAutomationCacheRequest_put_AutomationElementMode(cache_req, 0x02); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomationCacheRequest_put_AutomationElementMode(cache_req, AutomationElementMode_None); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + elem_mode = 0; + hr = IUIAutomationCacheRequest_get_AutomationElementMode(cache_req, &elem_mode); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(elem_mode == AutomationElementMode_None, "Unexpected element mode %#x\n", elem_mode); + + IUIAutomationCacheRequest_Release(cache_req); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -10655,6 +10820,7 @@ static void test_CUIAutomation(void)
test_CUIAutomation_condition_ifaces(uia_iface); test_CUIAutomation_value_conversion(uia_iface); + test_CUIAutomation_cache_request_iface(uia_iface); test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface);
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index f07e15817cc..598739746d6 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -593,6 +593,264 @@ static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condi return S_OK; }
+static HRESULT create_control_view_condition_iface(IUIAutomationCondition **out_condition) +{ + IUIAutomationCondition *prop_cond, *not_cond; + HRESULT hr; + VARIANT v; + + if (!out_condition) + return E_POINTER; + + *out_condition = NULL; + + VariantInit(&v); + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = VARIANT_FALSE; + hr = create_uia_property_condition_iface(&prop_cond, UIA_IsControlElementPropertyId, v, PropertyConditionFlags_None); + if (FAILED(hr)) + return hr; + + hr = create_uia_not_condition_iface(¬_cond, prop_cond); + if (FAILED(hr)) + { + IUIAutomationCondition_Release(prop_cond); + return hr; + } + + *out_condition = not_cond; + + return S_OK; +} + +/* + * IUIAutomationCacheRequest interface. + */ +struct uia_cache_request { + IUIAutomationCacheRequest IUIAutomationCacheRequest_iface; + LONG ref; + + IUIAutomationCondition *view_condition; + struct UiaCacheRequest cache_req; +}; + +static inline struct uia_cache_request *impl_from_IUIAutomationCacheRequest(IUIAutomationCacheRequest *iface) +{ + return CONTAINING_RECORD(iface, struct uia_cache_request, IUIAutomationCacheRequest_iface); +} + +static HRESULT WINAPI uia_cache_request_QueryInterface(IUIAutomationCacheRequest *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationCacheRequest) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationCacheRequest_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_cache_request_AddRef(IUIAutomationCacheRequest *iface) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + ULONG ref = InterlockedIncrement(&uia_cache_request->ref); + + TRACE("%p, refcount %ld\n", uia_cache_request, ref); + return ref; +} + +static ULONG WINAPI uia_cache_request_Release(IUIAutomationCacheRequest *iface) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + ULONG ref = InterlockedDecrement(&uia_cache_request->ref); + + TRACE("%p, refcount %ld\n", uia_cache_request, ref); + + if (!ref) + { + IUIAutomationCondition_Release(uia_cache_request->view_condition); + heap_free(uia_cache_request); + } + + return ref; +} + +static HRESULT WINAPI uia_cache_request_AddProperty(IUIAutomationCacheRequest *iface, PROPERTYID prop_id) +{ + FIXME("%p, %d: stub\n", iface, prop_id); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_AddPattern(IUIAutomationCacheRequest *iface, PATTERNID pattern_id) +{ + FIXME("%p, %d: stub\n", iface, pattern_id); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_Clone(IUIAutomationCacheRequest *iface, IUIAutomationCacheRequest **out_req) +{ + FIXME("%p, %p: stub\n", iface, out_req); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_cache_request_get_TreeScope(IUIAutomationCacheRequest *iface, enum TreeScope *scope) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, scope); + + if (!scope) + return E_POINTER; + + *scope = uia_cache_request->cache_req.Scope; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_TreeScope(IUIAutomationCacheRequest *iface, enum TreeScope scope) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %#x\n", iface, scope); + + if (!scope || (scope & ~TreeScope_SubTree)) + return E_INVALIDARG; + + if ((scope & TreeScope_Children) || (scope & TreeScope_Descendants)) + { + FIXME("Unimplemented TreeScope %#x\n", scope); + return E_NOTIMPL; + } + + uia_cache_request->cache_req.Scope = scope; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_get_TreeFilter(IUIAutomationCacheRequest *iface, + IUIAutomationCondition **filter) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, filter); + + if (!filter) + return E_POINTER; + + IUIAutomationCondition_AddRef(uia_cache_request->view_condition); + *filter = uia_cache_request->view_condition; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_TreeFilter(IUIAutomationCacheRequest *iface, IUIAutomationCondition *filter) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + struct UiaCondition *cond_struct; + HRESULT hr; + + TRACE("%p, %p\n", iface, filter); + + if (!filter) + return E_POINTER; + + hr = get_uia_condition_struct_from_iface(filter, &cond_struct); + if (FAILED(hr)) + return hr; + + uia_cache_request->cache_req.pViewCondition = cond_struct; + IUIAutomationCondition_Release(uia_cache_request->view_condition); + uia_cache_request->view_condition = filter; + IUIAutomationCondition_AddRef(filter); + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_get_AutomationElementMode(IUIAutomationCacheRequest *iface, + enum AutomationElementMode *mode) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %p\n", iface, mode); + + if (!mode) + return E_POINTER; + + *mode = uia_cache_request->cache_req.automationElementMode; + + return S_OK; +} + +static HRESULT WINAPI uia_cache_request_put_AutomationElementMode(IUIAutomationCacheRequest *iface, + enum AutomationElementMode mode) +{ + struct uia_cache_request *uia_cache_request = impl_from_IUIAutomationCacheRequest(iface); + + TRACE("%p, %d\n", iface, mode); + + if ((mode != AutomationElementMode_Full) && (mode != AutomationElementMode_None)) + return E_INVALIDARG; + + if (mode == AutomationElementMode_None) + { + FIXME("AutomationElementMode_None unsupported\n"); + return E_NOTIMPL; + } + + uia_cache_request->cache_req.automationElementMode = mode; + + return S_OK; +} + +static const IUIAutomationCacheRequestVtbl uia_cache_request_vtbl = { + uia_cache_request_QueryInterface, + uia_cache_request_AddRef, + uia_cache_request_Release, + uia_cache_request_AddProperty, + uia_cache_request_AddPattern, + uia_cache_request_Clone, + uia_cache_request_get_TreeScope, + uia_cache_request_put_TreeScope, + uia_cache_request_get_TreeFilter, + uia_cache_request_put_TreeFilter, + uia_cache_request_get_AutomationElementMode, + uia_cache_request_put_AutomationElementMode, +}; + +static HRESULT create_uia_cache_request_iface(IUIAutomationCacheRequest **out_cache_req) +{ + struct uia_cache_request *uia_cache_request; + IUIAutomationCondition *view_condition; + HRESULT hr; + + if (!out_cache_req) + return E_POINTER; + + *out_cache_req = NULL; + hr = create_control_view_condition_iface(&view_condition); + if (FAILED(hr)) + return hr; + + uia_cache_request = heap_alloc_zero(sizeof(*uia_cache_request)); + if (!uia_cache_request) + { + IUIAutomationCondition_Release(view_condition); + return E_OUTOFMEMORY; + } + + uia_cache_request->IUIAutomationCacheRequest_iface.lpVtbl = &uia_cache_request_vtbl; + uia_cache_request->ref = 1; + + uia_cache_request->view_condition = view_condition; + get_uia_condition_struct_from_iface(view_condition, &uia_cache_request->cache_req.pViewCondition); + uia_cache_request->cache_req.Scope = TreeScope_Element; + uia_cache_request->cache_req.automationElementMode = AutomationElementMode_Full; + + *out_cache_req = &uia_cache_request->IUIAutomationCacheRequest_iface; + return S_OK; +} + /* * IUIAutomationElementArray interface. */ @@ -1969,34 +2227,9 @@ static HRESULT WINAPI uia_iface_get_RawViewCondition(IUIAutomation6 *iface, IUIA
static HRESULT WINAPI uia_iface_get_ControlViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - IUIAutomationCondition *prop_cond, *not_cond; - HRESULT hr; - VARIANT v; - TRACE("%p, %p\n", iface, out_condition);
- if (!out_condition) - return E_POINTER; - - *out_condition = NULL; - - VariantInit(&v); - V_VT(&v) = VT_BOOL; - V_BOOL(&v) = VARIANT_FALSE; - hr = create_uia_property_condition_iface(&prop_cond, UIA_IsControlElementPropertyId, v, PropertyConditionFlags_None); - if (FAILED(hr)) - return hr; - - hr = create_uia_not_condition_iface(¬_cond, prop_cond); - if (FAILED(hr)) - { - IUIAutomationCondition_Release(prop_cond); - return hr; - } - - *out_condition = not_cond; - - return S_OK; + return create_control_view_condition_iface(out_condition); }
static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) @@ -2007,8 +2240,9 @@ static HRESULT WINAPI uia_iface_get_ContentViewCondition(IUIAutomation6 *iface,
static HRESULT WINAPI uia_iface_CreateCacheRequest(IUIAutomation6 *iface, IUIAutomationCacheRequest **out_cache_req) { - FIXME("%p, %p: stub\n", iface, out_cache_req); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_cache_req); + + return create_uia_cache_request_iface(out_cache_req); }
static HRESULT WINAPI uia_iface_CreateTrueCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition)