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,