From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 185 +++++++++++++++++---- dlls/uiautomationcore/uia_client.c | 39 +++++ dlls/uiautomationcore/uia_ids.c | 3 +- 3 files changed, 198 insertions(+), 29 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 51fd04bfc02..888d8e39002 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1128,8 +1128,10 @@ static struct Provider HWND override_hwnd; struct Provider_prop_override *prop_override; int prop_override_count; + struct UiaRect bounds_rect; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; +static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links);
static const WCHAR *uia_bstr_prop_str = L"uia-string"; static const ULONG uia_i4_prop_val = 0xdeadbeef; @@ -1187,6 +1189,7 @@ enum { FRAG_NAVIGATE, FRAG_GET_RUNTIME_ID, FRAG_GET_FRAGMENT_ROOT, + FRAG_GET_BOUNDING_RECT, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, };
@@ -1197,6 +1200,7 @@ static const char *prov_method_str[] = { "Navigate", "GetRuntimeId", "get_FragmentRoot", + "get_BoundingRectangle", "GetOverrideProviderForHwnd", };
@@ -1222,6 +1226,31 @@ struct prov_method_sequence { static int sequence_cnt, sequence_size; static struct prov_method_sequence *sequence;
+/* + * This sequence of method calls is always used when creating an HUIANODE from + * an IRawElementProviderSimple. + */ +#define NODE_CREATE_SEQ(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ + { prov , PROV_GET_PROPERTY_VALUE }, \ + { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + +/* + * This sequence of method calls is always used when creating an HUIANODE from + * an IRawElementProviderSimple that returns an HWND from get_HostRawElementProvider. + */ +#define NODE_CREATE_SEQ2(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ + { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + static void flush_method_sequence(void) { HeapFree(GetProcessHeap(), 0, sequence); @@ -1891,8 +1920,15 @@ static HRESULT WINAPI ProviderFragment_GetRuntimeId(IRawElementProviderFragment static HRESULT WINAPI ProviderFragment_get_BoundingRectangle(IRawElementProviderFragment *iface, struct UiaRect *ret_val) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + struct Provider *This = impl_from_ProviderFragment(iface); + + add_method_call(This, FRAG_GET_BOUNDING_RECT); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + + *ret_val = This->bounds_rect; + return S_OK; }
static HRESULT WINAPI ProviderFragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface, @@ -4663,6 +4699,80 @@ static const struct prov_method_sequence get_elem_arr_prop_seq[] = { { 0 } };
+static const struct prov_method_sequence get_bounding_rect_seq[] = { + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_GET_BOUNDING_RECT }, + /* + * Win10v21H2+ and above call these, attempting to get the fragment root's + * HWND. I'm guessing this is an attempt to get the HWND's DPI for DPI scaling. + */ + { &Provider_child, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, + { 0 } +}; + +static const struct prov_method_sequence get_empty_bounding_rect_seq[] = { + { &Provider_child, FRAG_GET_BOUNDING_RECT }, + { 0 } +}; + +static void set_uia_rect(struct UiaRect *rect, double left, double top, double width, double height) +{ + rect->left = left; + rect->top = top; + rect->width = width; + rect->height = height; +} + +#define check_uia_rect_val( v, rect ) \ + check_uia_rect_val_( (v), (rect), __FILE__, __LINE__) +static void check_uia_rect_val_(VARIANT *v, struct UiaRect *rect, const char *file, int line) +{ + LONG lbound, ubound, elems, idx; + SAFEARRAY *sa; + double tmp[4]; + VARTYPE vt; + HRESULT hr; + UINT dims; + + ok_(file, line)(V_VT(v) == (VT_R8 | VT_ARRAY), "Unexpected rect VT hr %d.\n", V_VT(v)); + if (V_VT(v) != (VT_R8 | VT_ARRAY)) + return; + + sa = V_ARRAY(v); + hr = SafeArrayGetVartype(sa, &vt); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(vt == VT_R8, "Unexpected vt %d\n", vt); + + dims = SafeArrayGetDim(sa); + ok_(file, line)(dims == 1, "Unexpected dims %d\n", dims); + + lbound = ubound = elems = 0; + hr = SafeArrayGetLBound(sa, 1, &lbound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetLBound\n", hr); + ok_(file, line)(lbound == 0, "Unexpected lbound %ld\n", lbound); + + hr = SafeArrayGetUBound(sa, 1, &ubound); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetUBound\n", hr); + ok_(file, line)(ubound == 3, "Unexpected ubound %ld\n", ubound); + + elems = (ubound - lbound) + 1; + ok_(file, line)(elems == 4, "Unexpected rect elems %ld\n", elems); + + for (idx = 0; idx < ARRAY_SIZE(tmp); idx++) + { + hr = SafeArrayGetElement(sa, &idx, &tmp[idx]); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx for SafeArrayGetElement at idx %ld.\n", hr, idx); + } + + ok_(file, line)(tmp[0] == rect->left, "Unexpected left value %f, expected %f\n", tmp[0], rect->left); + ok_(file, line)(tmp[1] == rect->top, "Unexpected top value %f, expected %f\n", tmp[1], rect->top); + ok_(file, line)(tmp[2] == rect->width, "Unexpected width value %f, expected %f\n", tmp[2], rect->width); + ok_(file, line)(tmp[3] == rect->height, "Unexpected height value %f, expected %f\n", tmp[3], rect->height); +} + static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v) { LONG idx; @@ -4858,6 +4968,7 @@ static const struct uia_element_property element_properties[] = { static void test_UiaGetPropertyValue(void) { const struct uia_element_property *elem_prop; + struct UiaRect rect; IUnknown *unk_ns; unsigned int i; HUIANODE node; @@ -4920,9 +5031,51 @@ static void test_UiaGetPropertyValue(void) winetest_pop_context(); }
- Provider.ret_invalid_prop_type = FALSE; ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + + /* + * Windows 7 will call get_FragmentRoot in an endless loop until the fragment root returns an HWND. + * It's the only version with this behavior. + */ + if (!UiaLookupId(AutomationIdentifierType_Property, &OptimizeForVisualContent_Property_GUID)) + { + win_skip("Skipping UIA_BoundingRectanglePropertyId tests for Win7\n"); + goto exit; + } + + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); + node = (void *)0xdeadbeef; + hr = UiaNodeFromProvider(&Provider_child.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + + /* Non-empty bounding rectangle, will return a VT_R8 SAFEARRAY. */ + set_uia_rect(&rect, 0, 0, 50, 50); + Provider_child.bounds_rect = rect; + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_rect_val(&v, &rect); + VariantClear(&v); + + ok_method_sequence(get_bounding_rect_seq, "get_bounding_rect_seq"); + + /* Empty bounding rectangle will return ReservedNotSupportedValue. */ + set_uia_rect(&rect, 0, 0, 0, 0); + Provider_child.bounds_rect = rect; + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v)); + VariantClear(&v); + ok_method_sequence(get_empty_bounding_rect_seq, "get_empty_bounding_rect_seq"); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); + +exit:
IUnknown_Release(unk_ns); CoUninitialize(); @@ -6994,19 +7147,6 @@ static void test_cache_req_sa_(SAFEARRAY *sa, LONG exp_lbound[2], LONG exp_elems } }
-/* - * This sequence of method calls is always used when creating an HUIANODE from - * an IRawElementProviderSimple. - */ -#define NODE_CREATE_SEQ(prov) \ - { prov , PROV_GET_PROVIDER_OPTIONS }, \ - /* Win10v1507 and below call this. */ \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ - { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ - { prov , PROV_GET_PROPERTY_VALUE }, \ - { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ - static const struct prov_method_sequence cache_req_seq1[] = { { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId. */ { 0 } @@ -7505,18 +7645,6 @@ static void test_UiaGetUpdatedCache(void) CoUninitialize(); }
-/* - * This sequence of method calls is always used when creating an HUIANODE from - * an IRawElementProviderSimple that returns an HWND from get_HostRawElementProvider. - */ -#define NODE_CREATE_SEQ2(prov) \ - { prov , PROV_GET_PROVIDER_OPTIONS }, \ - /* Win10v1507 and below call this. */ \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ - { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ - { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ - static const struct prov_method_sequence nav_seq1[] = { NODE_CREATE_SEQ2(&Provider), { &Provider, PROV_GET_PROVIDER_OPTIONS }, @@ -8333,6 +8461,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, prov->override_hwnd = NULL; prov->prop_override = NULL; prov->prop_override_count = 0; + memset(&prov->bounds_rect, 0, sizeof(prov->bounds_rect)); if (initialize_nav_links) { prov->frag_root = NULL; diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index eb49640a61f..793df3b6618 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1391,6 +1391,45 @@ static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov, break; }
+ case UIA_BoundingRectanglePropertyId: + { + IRawElementProviderFragment *elfrag; + struct UiaRect rect = { 0 }; + double rect_vals[4]; + SAFEARRAY *sa; + LONG idx; + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag); + if (FAILED(hr) || !elfrag) + break; + + hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect); + IRawElementProviderFragment_Release(elfrag); + if (FAILED(hr) || (rect.width <= 0 || rect.height <= 0)) + break; + + if (!(sa = SafeArrayCreateVector(VT_R8, 0, ARRAY_SIZE(rect_vals)))) + break; + + rect_vals[0] = rect.left; + rect_vals[1] = rect.top; + rect_vals[2] = rect.width; + rect_vals[3] = rect.height; + for (idx = 0; idx < ARRAY_SIZE(rect_vals); idx++) + { + hr = SafeArrayPutElement(sa, &idx, &rect_vals[idx]); + if (FAILED(hr)) + { + SafeArrayDestroy(sa); + break; + } + } + + V_VT(ret_val) = VT_R8 | VT_ARRAY; + V_ARRAY(ret_val) = sa; + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index a3f414110d0..6f5b00cd5ca 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -225,7 +225,8 @@ static const struct uia_prop_info default_uia_properties[] = { PROP_TYPE_ELEM_PROP, UIAutomationType_Bool, }, { &IsWindowPatternAvailable_Property_GUID, UIA_IsWindowPatternAvailablePropertyId, }, { &RangeValue_Minimum_Property_GUID, UIA_RangeValueMinimumPropertyId, }, - { &BoundingRectangle_Property_GUID, UIA_BoundingRectanglePropertyId, }, + { &BoundingRectangle_Property_GUID, UIA_BoundingRectanglePropertyId, + PROP_TYPE_SPECIAL, UIAutomationType_Rect, }, { &LegacyIAccessible_Value_Property_GUID, UIA_LegacyIAccessibleValuePropertyId, }, { &IsDragPatternAvailable_Property_GUID, UIA_IsDragPatternAvailablePropertyId, }, { &DescribedBy_Property_GUID, UIA_DescribedByPropertyId,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 129 ++++++++++++++++++++- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 43 +++++++ dlls/uiautomationcore/uia_ids.c | 23 +++- dlls/uiautomationcore/uia_private.h | 8 ++ dlls/uiautomationcore/uia_provider.c | 6 - 6 files changed, 201 insertions(+), 9 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 888d8e39002..0ce2693d479 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1103,12 +1103,19 @@ struct Provider_prop_override VARIANT val; };
+struct Provider_value_pattern_data +{ + BOOL is_supported; + BOOL is_read_only; +}; + static struct Provider { IRawElementProviderSimple IRawElementProviderSimple_iface; IRawElementProviderFragment IRawElementProviderFragment_iface; IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface; IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface; + IValueProvider IValueProvider_iface; LONG ref;
const char *prov_name; @@ -1129,6 +1136,7 @@ static struct Provider struct Provider_prop_override *prop_override; int prop_override_count; struct UiaRect bounds_rect; + struct Provider_value_pattern_data value_pattern_data; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); @@ -1184,6 +1192,7 @@ static SAFEARRAY *create_unk_safearray(void)
enum { PROV_GET_PROVIDER_OPTIONS, + PROV_GET_PATTERN_PROV, PROV_GET_PROPERTY_VALUE, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, FRAG_NAVIGATE, @@ -1195,6 +1204,7 @@ enum {
static const char *prov_method_str[] = { "get_ProviderOptions", + "GetPatternProvider", "GetPropertyValue", "get_HostRawElementProvider", "Navigate", @@ -1544,6 +1554,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R *ppv = &This->IRawElementProviderFragmentRoot_iface; else if (IsEqualIID(riid, &IID_IRawElementProviderHwndOverride)) *ppv = &This->IRawElementProviderHwndOverride_iface; + else if (IsEqualIID(riid, &IID_IValueProvider)) + *ppv = &This->IValueProvider_iface; else return E_NOINTERFACE;
@@ -1586,8 +1598,29 @@ HRESULT WINAPI ProviderSimple_get_ProviderOptions(IRawElementProviderSimple *ifa HRESULT WINAPI ProviderSimple_GetPatternProvider(IRawElementProviderSimple *iface, PATTERNID pattern_id, IUnknown **ret_val) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + struct Provider *This = impl_from_ProviderSimple(iface); + + add_method_call(This, PROV_GET_PATTERN_PROV); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + + *ret_val = NULL; + switch (pattern_id) + { + case UIA_ValuePatternId: + if (This->value_pattern_data.is_supported) + *ret_val = (IUnknown *)iface; + break; + + default: + break; + } + + if (*ret_val) + IUnknown_AddRef(*ret_val); + + return S_OK; }
HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface, @@ -2071,12 +2104,67 @@ static const IRawElementProviderHwndOverrideVtbl ProviderHwndOverrideVtbl = { ProviderHwndOverride_GetOverrideProviderForHwnd, };
+static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface) +{ + return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface); +} + +static HRESULT WINAPI ProviderValuePattern_QueryInterface(IValueProvider *iface, REFIID riid, + void **ppv) +{ + struct Provider *Provider = impl_from_ProviderValuePattern(iface); + return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI ProviderValuePattern_AddRef(IValueProvider *iface) +{ + struct Provider *Provider = impl_from_ProviderValuePattern(iface); + return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI ProviderValuePattern_Release(IValueProvider *iface) +{ + struct Provider *Provider = impl_from_ProviderValuePattern(iface); + return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI ProviderValuePattern_SetValue(IValueProvider *iface, LPCWSTR val) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderValuePattern_get_Value(IValueProvider *iface, BSTR *ret_val) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderValuePattern_get_IsReadOnly(IValueProvider *iface, BOOL *ret_val) +{ + struct Provider *Provider = impl_from_ProviderValuePattern(iface); + + *ret_val = Provider->value_pattern_data.is_read_only; + + return S_OK; +} + +static const IValueProviderVtbl ProviderValuePatternVtbl = { + ProviderValuePattern_QueryInterface, + ProviderValuePattern_AddRef, + ProviderValuePattern_Release, + ProviderValuePattern_SetValue, + ProviderValuePattern_get_Value, + ProviderValuePattern_get_IsReadOnly, +}; + static struct Provider Provider = { { &ProviderSimpleVtbl }, { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider", NULL, NULL, @@ -2091,6 +2179,7 @@ static struct Provider Provider2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider2", NULL, NULL, @@ -2105,6 +2194,7 @@ static struct Provider Provider_child = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_child", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, @@ -2119,6 +2209,7 @@ static struct Provider Provider_child2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_child2", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, @@ -2133,6 +2224,7 @@ static struct Provider Provider_hwnd = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_hwnd", NULL, NULL, @@ -2147,6 +2239,7 @@ static struct Provider Provider_nc = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_nc", NULL, NULL, @@ -2162,6 +2255,7 @@ static struct Provider Provider_proxy = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_proxy", NULL, NULL, @@ -2177,6 +2271,7 @@ static struct Provider Provider_proxy2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_proxy2", NULL, NULL, @@ -2192,6 +2287,7 @@ static struct Provider Provider_override = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderValuePatternVtbl }, 1, "Provider_override", NULL, NULL, @@ -2208,6 +2304,7 @@ static struct Provider Provider_override = { &ProviderFragmentVtbl }, \ { &ProviderFragmentRootVtbl }, \ { &ProviderHwndOverrideVtbl }, \ + { &ProviderValuePatternVtbl }, \ 1, \ "Provider_" # name "", \ NULL, NULL, \ @@ -4699,6 +4796,11 @@ static const struct prov_method_sequence get_elem_arr_prop_seq[] = { { 0 } };
+static const struct prov_method_sequence get_pattern_prop_seq[] = { + { &Provider, PROV_GET_PATTERN_PROV }, + { 0 } +}; + static const struct prov_method_sequence get_bounding_rect_seq[] = { NODE_CREATE_SEQ(&Provider_child), { &Provider_child, FRAG_GET_BOUNDING_RECT }, @@ -5031,6 +5133,28 @@ static void test_UiaGetPropertyValue(void) winetest_pop_context(); }
+ /* IValueProvider pattern property IDs. */ + Provider.value_pattern_data.is_supported = FALSE; + hr = UiaGetPropertyValue(node, UIA_ValueIsReadOnlyPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v)); + ok_method_sequence(get_pattern_prop_seq, NULL); + VariantClear(&v); + + Provider.value_pattern_data.is_supported = TRUE; + for (i = 0; i < 2; i++) + { + Provider.value_pattern_data.is_read_only = i; + + hr = UiaGetPropertyValue(node, UIA_ValueIsReadOnlyPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "Unexpected VT %d\n", V_VT(&v)); + ok(check_variant_bool(&v, i), "Unexpected BOOL %#x\n", V_BOOL(&v)); + ok_method_sequence(get_pattern_prop_seq, NULL); + VariantClear(&v); + } + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); @@ -8462,6 +8586,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, prov->prop_override = NULL; prov->prop_override_count = 0; memset(&prov->bounds_rect, 0, sizeof(prov->bounds_rect)); + memset(&prov->value_pattern_data, 0, sizeof(prov->value_pattern_data)); if (initialize_nav_links) { prov->frag_root = NULL; diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 9954b465f90..f0a20d61579 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -25,6 +25,7 @@ struct uia_prop_info { int prop_id; int prop_type; int type; + int pattern_id; };
struct uia_event_info { diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 793df3b6618..ce2aeb21622 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1437,6 +1437,46 @@ static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov, return S_OK; }
+static HRESULT uia_provider_get_pattern_prop_val(struct uia_provider *prov, + const struct uia_prop_info *prop_info, VARIANT *ret_val) +{ + const struct uia_pattern_info *pattern_info = uia_pattern_info_from_id(prop_info->pattern_id); + IUnknown *unk, *pattern_prov; + HRESULT hr; + + unk = pattern_prov = NULL; + hr = IRawElementProviderSimple_GetPatternProvider(prov->elprov, prop_info->pattern_id, &unk); + if (FAILED(hr) || !unk) + return S_OK; + + hr = IUnknown_QueryInterface(unk, pattern_info->pattern_iid, (void **)&pattern_prov); + IUnknown_Release(unk); + if (FAILED(hr) || !pattern_prov) + { + WARN("Failed to get pattern interface from object\n"); + return S_OK; + } + + switch (prop_info->prop_id) + { + case UIA_ValueIsReadOnlyPropertyId: + { + BOOL val; + + hr = IValueProvider_get_IsReadOnly((IValueProvider *)pattern_prov, &val); + if (SUCCEEDED(hr)) + variant_init_bool(ret_val, val); + break; + } + + default: + break; + } + + IUnknown_Release(pattern_prov); + return S_OK; +} + static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface, const struct uia_prop_info *prop_info, VARIANT *ret_val) { @@ -1453,6 +1493,9 @@ static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface, case PROP_TYPE_SPECIAL: return uia_provider_get_special_prop_val(prov, prop_info, ret_val);
+ case PROP_TYPE_PATTERN_PROP: + return uia_provider_get_pattern_prop_val(prov, prop_info, ret_val); + default: break; } diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index 6f5b00cd5ca..7ad8da69cd4 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -97,7 +97,9 @@ static const struct uia_prop_info default_uia_properties[] = { { &IsDropTargetPatternAvailable_Property_GUID, UIA_IsDropTargetPatternAvailablePropertyId, }, { &Dock_DockPosition_Property_GUID, UIA_DockDockPositionPropertyId, }, { &Styles_StyleId_Property_GUID, UIA_StylesStyleIdPropertyId, }, - { &Value_IsReadOnly_Property_GUID, UIA_ValueIsReadOnlyPropertyId, }, + { &Value_IsReadOnly_Property_GUID, UIA_ValueIsReadOnlyPropertyId, + PROP_TYPE_PATTERN_PROP, UIAutomationType_Bool, + UIA_ValuePatternId, }, { &IsSpreadsheetPatternAvailable_Property_GUID, UIA_IsSpreadsheetPatternAvailablePropertyId, }, { &Styles_StyleName_Property_GUID, UIA_StylesStyleNamePropertyId, }, { &IsAnnotationPatternAvailable_Property_GUID, UIA_IsAnnotationPatternAvailablePropertyId, }, @@ -482,6 +484,17 @@ static const struct uia_pattern_info default_uia_patterns[] = { &IID_ISpreadsheetItemProvider, }, };
+static const int pattern_id_idx[] = { + 0x1f, 0x1d, 0x10, 0x0c, 0x15, 0x13, 0x19, 0x0b, + 0x1c, 0x04, 0x1e, 0x06, 0x0f, 0x17, 0x09, 0x0a, + 0x1b, 0x00, 0x11, 0x02, 0x05, 0x14, 0x20, 0x1a, + 0x12, 0x07, 0x18, 0x21, 0x01, 0x16, 0x03, 0x08, + 0x0d, 0x0e, +}; + +#define PATTERN_ID_MIN 10000 +#define PATTERN_ID_MAX (PATTERN_ID_MIN + ARRAY_SIZE(default_uia_patterns)) + static const struct uia_pattern_info *uia_pattern_info_from_guid(const GUID *guid) { struct uia_pattern_info *pattern; @@ -493,6 +506,14 @@ static const struct uia_pattern_info *uia_pattern_info_from_guid(const GUID *gui return NULL; }
+const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) +{ + if ((pattern_id < PATTERN_ID_MIN) || (pattern_id > PATTERN_ID_MAX)) + return NULL; + + return &default_uia_patterns[pattern_id_idx[pattern_id - PATTERN_ID_MIN]]; +} + /*********************************************************************** * UiaLookupId (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 63820d0b937..2478fcc1bf0 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -28,6 +28,7 @@ enum uia_prop_type { PROP_TYPE_UNKNOWN, PROP_TYPE_ELEM_PROP, PROP_TYPE_SPECIAL, + PROP_TYPE_PATTERN_PROP, };
/* @@ -91,6 +92,12 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); }
+static inline void variant_init_bool(VARIANT *v, BOOL val) +{ + V_VT(v) = VT_BOOL; + V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; +} + /* uia_client.c */ int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; @@ -99,6 +106,7 @@ HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; +const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) DECLSPEC_HIDDEN;
/* uia_provider.c */ void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index fc718fabe43..c851ecaf685 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -35,12 +35,6 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
-static void variant_init_bool(VARIANT *v, BOOL val) -{ - V_VT(v) = VT_BOOL; - V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; -} - static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, ULONG flag) { HRESULT hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 79 ++++++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 30 +++++++- 2 files changed, 107 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 0ce2693d479..b30e592555c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -3440,7 +3440,9 @@ static void test_UiaProviderFromIAccessible(void) { ILegacyIAccessibleProvider *accprov; IRawElementProviderSimple *elprov, *elprov2; + IRawElementProviderFragment *elfrag; enum ProviderOptions prov_opt; + struct UiaRect rect; IAccessible *acc; IUnknown *unk; WNDCLASSA cls; @@ -3958,6 +3960,83 @@ static void test_UiaProviderFromIAccessible(void) IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+ /* + * Test IRawElementProviderFragment_get_BoundingRectangle. + */ + set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 0, L"acc_name", 25, 25, 50, 50); + /* Test the case where Accessible is not the root for its HWND. */ + acc_client = NULL; + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)&elfrag); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!elfrag, "elfrag == NULL\n"); + + SET_EXPECT(winproc_GETOBJECT_CLIENT); + SET_EXPECT(Accessible_get_accRole); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(rect.left == (double)Accessible.left, "Unexpected left value %f\n", rect.left); + ok(rect.top == (double)Accessible.top, "Unexpected top value %f\n", rect.top); + ok(rect.width == (double)Accessible.width, "Unexpected width value %f\n", rect.width); + ok(rect.height == (double)Accessible.height, "Unexpected height value %f\n", rect.height); + CHECK_CALLED(winproc_GETOBJECT_CLIENT); + CHECK_CALLED(Accessible_get_accRole); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* If Accessible has STATE_SYSTEM_OFFSCREEN, it will return an empty rect. */ + set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_OFFSCREEN, 0, L"acc_name", 0, 0, 50, 50); + SET_EXPECT(Accessible_get_accState); + hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(rect.left == 0.0, "Unexpected left value %f\n", rect.left); + ok(rect.top == 0.0, "Unexpected top value %f\n", rect.top); + ok(rect.width == 0.0, "Unexpected width value %f\n", rect.width); + ok(rect.height == 0.0, "Unexpected height value %f\n", rect.height); + CHECK_CALLED(Accessible_get_accState); + + IRawElementProviderFragment_Release(elfrag); + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + /* Test case where accessible is the root accessible. */ + set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, 0, 0, L"acc_name", 0, 0, 0, 0); + acc_client = &Accessible.IAccessible_iface; + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)&elfrag); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!elfrag, "elfrag == NULL\n"); + + SET_EXPECT(winproc_GETOBJECT_CLIENT); + hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(rect.left == 0.0, "Unexpected left value %f\n", rect.left); + ok(rect.top == 0.0, "Unexpected top value %f\n", rect.top); + ok(rect.width == 0.0, "Unexpected width value %f\n", rect.width); + ok(rect.height == 0.0, "Unexpected height value %f\n", rect.height); + CHECK_CALLED(winproc_GETOBJECT_CLIENT); + + /* Second call does nothing. */ + hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(rect.left == 0.0, "Unexpected left value %f\n", rect.left); + ok(rect.top == 0.0, "Unexpected top value %f\n", rect.top); + ok(rect.width == 0.0, "Unexpected width value %f\n", rect.width); + ok(rect.height == 0.0, "Unexpected height value %f\n", rect.height); + + IRawElementProviderFragment_Release(elfrag); + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + acc_client = NULL; + test_uia_prov_from_acc_properties(); test_uia_prov_from_acc_navigation(); test_uia_prov_from_acc_ia2(); diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index c851ecaf685..c4fbd181ada 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -868,8 +868,34 @@ static HRESULT WINAPI msaa_fragment_GetRuntimeId(IRawElementProviderFragment *if static HRESULT WINAPI msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment *iface, struct UiaRect *ret_val) { - FIXME("%p, %p: stub!\n", iface, ret_val); - return E_NOTIMPL; + struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface); + LONG left, top, width, height; + HRESULT hr; + + TRACE("%p, %p\n", iface, ret_val); + + memset(ret_val, 0, sizeof(*ret_val)); + + /* + * If this IAccessible is at the root of its HWND, the BaseHwnd provider + * will supply the bounding rectangle. + */ + if (msaa_check_root_acc(msaa_prov)) + return S_OK; + + if (msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, STATE_SYSTEM_OFFSCREEN)) + return S_OK; + + hr = IAccessible_accLocation(msaa_prov->acc, &left, &top, &width, &height, msaa_prov->cid); + if (FAILED(hr)) + return hr; + + ret_val->left = left; + ret_val->top = top; + ret_val->width = width; + ret_val->height = height; + + return S_OK; }
static HRESULT WINAPI msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface,
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 28 ++++++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 13 ++++++++++ 2 files changed, 41 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b30e592555c..f0112cd2ffb 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -3434,6 +3434,34 @@ static void test_uia_prov_from_acc_properties(void)
IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + /* UIA_NamePropertyId tests. */ + set_accessible_props(&Accessible, 0, 0, 0, L"Accessible", 0, 0, 0, 0); + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + SET_EXPECT(Accessible_get_accName); + VariantInit(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_NamePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), Accessible.name), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + CHECK_CALLED(Accessible_get_accName); + + /* Name is not cached. */ + set_accessible_props(&Accessible, 0, 0, 0, L"Accessible2", 0, 0, 0, 0); + SET_EXPECT(Accessible_get_accName); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_NamePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), Accessible.name), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + CHECK_CALLED(Accessible_get_accName); + + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); }
static void test_UiaProviderFromIAccessible(void) diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index c4fbd181ada..58d1db9bae8 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -664,6 +664,19 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, STATE_SYSTEM_PROTECTED)); break;
+ case UIA_NamePropertyId: + { + BSTR name; + + hr = IAccessible_get_accName(msaa_prov->acc, msaa_prov->cid, &name); + if (SUCCEEDED(hr) && name) + { + V_VT(ret_val) = VT_BSTR; + V_BSTR(ret_val) = name; + } + break; + } + default: FIXME("Unimplemented propertyId %d\n", prop_id); break;
This merge request was approved by Esme Povirk.