-- v2: uiautomationcore: Implement ILegacyIAccessibleProvider::get_Role for MSAA providers. uiautomationcore: Add support for UIA_LegacyIAccessibleRolePropertyId. uiautomationcore: Add support for UIA_LegacyIAccessibleChildIdPropertyId. uiautomationcore: Add support for UIA_IsOffscreenPropertyId to MSAA providers.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 83 ++++++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 39 ++++++++++ 2 files changed, 122 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ce3ee303fd1..2971f356688 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -3383,6 +3383,7 @@ static void test_uia_prov_from_acc_navigation(void) static void test_uia_prov_from_acc_properties(void) { IRawElementProviderSimple *elprov; + RECT rect[2] = { 0 }; HRESULT hr; VARIANT v; int i, x; @@ -3476,6 +3477,88 @@ static void test_uia_prov_from_acc_properties(void) } Accessible.state = 0;
+ /* + * UIA_IsOffscreenPropertyId relies upon either STATE_SYSTEM_OFFSCREEN + * being set, or accLocation returning a location that is within the + * client area bounding box of the HWND it is contained within. + */ + set_accessible_props(&Accessible, 0, STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", 0, 0, 0, 0); + SET_EXPECT(Accessible_get_accState); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + + /* accLocation fails, will return FALSE. */ + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", 0, 0, 0, 0); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Window is visible, Accessible is within its bounds. */ + ShowWindow(Accessible.ow_hwnd, SW_SHOW); + ok(GetClientRect(Accessible.ow_hwnd, &rect[0]), "GetClientRect returned FALSE\n"); + MapWindowPoints(Accessible.ow_hwnd, NULL, (POINT *)&rect[0], 2); + + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].left, rect[0].top, + (rect[0].right - rect[0].left), (rect[0].bottom - rect[0].top)); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &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, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* + * Window is invisible, Accessible is within its bounds. Window visibility + * doesn't effect whether or not an IAccessible is considered offscreen. + */ + ShowWindow(Accessible.ow_hwnd, SW_HIDE); + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].left, rect[0].top, + (rect[0].right - rect[0].left), (rect[0].bottom - rect[0].top)); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &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, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Accessible now outside of its window's bounds. */ + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].right, rect[0].bottom, + 10, 10); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Accessible within window bounds, but not client area bounds. */ + ok(GetWindowRect(Accessible.ow_hwnd, &rect[1]), "GetClientRect returned FALSE\n"); + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[1].left, rect[1].top, + rect[0].left - 1, rect[0].top - 1); + + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index a7d41248abc..119a7e69abc 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -676,6 +676,45 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, break; }
+ case UIA_IsOffscreenPropertyId: + { + RECT rect[2] = { 0 }; + RECT intersect_rect; + LONG width, height; + + variant_init_bool(ret_val, FALSE); + if (msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, STATE_SYSTEM_OFFSCREEN)) + { + variant_init_bool(ret_val, TRUE); + break; + } + + hr = IAccessible_accLocation(msaa_prov->acc, &rect[0].left, &rect[0].top, &width, &height, msaa_prov->cid); + if (FAILED(hr)) + break; + + rect[0].right = rect[0].left + width; + rect[0].bottom = rect[0].top + height; + SetLastError(NOERROR); + if (!GetClientRect(msaa_prov->hwnd, &rect[1])) + { + if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) + variant_init_bool(ret_val, TRUE); + break; + } + + SetLastError(NOERROR); + if (!MapWindowPoints(msaa_prov->hwnd, NULL, (POINT *)&rect[1], 2) && GetLastError()) + { + if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) + variant_init_bool(ret_val, TRUE); + break; + } + + variant_init_bool(ret_val, !IntersectRect(&intersect_rect, &rect[0], &rect[1])); + break; + } + default: FIXME("Unimplemented propertyId %d\n", prop_id); break;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 189 +++++++++++++++++++++ dlls/uiautomationcore/uia_client.c | 10 ++ dlls/uiautomationcore/uia_ids.c | 4 +- dlls/uiautomationcore/uia_private.h | 6 + dlls/uiautomationcore/uia_provider.c | 6 - 5 files changed, 208 insertions(+), 7 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 2971f356688..8f28271b25b 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1115,6 +1115,12 @@ struct Provider_value_pattern_data BOOL is_read_only; };
+struct Provider_legacy_accessible_pattern_data +{ + BOOL is_supported; + int child_id; +}; + static struct Provider { IRawElementProviderSimple IRawElementProviderSimple_iface; @@ -1122,6 +1128,7 @@ static struct Provider IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface; IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface; IValueProvider IValueProvider_iface; + ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface; LONG ref;
const char *prov_name; @@ -1143,6 +1150,7 @@ static struct Provider int prop_override_count; struct UiaRect bounds_rect; struct Provider_value_pattern_data value_pattern_data; + struct Provider_legacy_accessible_pattern_data legacy_acc_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); @@ -1571,6 +1579,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R *ppv = &This->IRawElementProviderHwndOverride_iface; else if (IsEqualIID(riid, &IID_IValueProvider)) *ppv = &This->IValueProvider_iface; + else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider)) + *ppv = &This->ILegacyIAccessibleProvider_iface; else return E_NOINTERFACE;
@@ -1628,6 +1638,11 @@ HRESULT WINAPI ProviderSimple_GetPatternProvider(IRawElementProviderSimple *ifac *ret_val = (IUnknown *)iface; break;
+ case UIA_LegacyIAccessiblePatternId: + if (This->legacy_acc_pattern_data.is_supported) + *ret_val = (IUnknown *)iface; + break; + default: break; } @@ -2176,6 +2191,141 @@ static const IValueProviderVtbl ProviderValuePatternVtbl = { ProviderValuePattern_get_IsReadOnly, };
+static inline struct Provider *impl_from_ProviderLegacyIAccessiblePattern(ILegacyIAccessibleProvider *iface) +{ + return CONTAINING_RECORD(iface, struct Provider, ILegacyIAccessibleProvider_iface); +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_QueryInterface(ILegacyIAccessibleProvider *iface, REFIID riid, + void **ppv) +{ + struct Provider *Provider = impl_from_ProviderLegacyIAccessiblePattern(iface); + return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI ProviderLegacyIAccessiblePattern_AddRef(ILegacyIAccessibleProvider *iface) +{ + struct Provider *Provider = impl_from_ProviderLegacyIAccessiblePattern(iface); + return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI ProviderLegacyIAccessiblePattern_Release(ILegacyIAccessibleProvider *iface) +{ + struct Provider *Provider = impl_from_ProviderLegacyIAccessiblePattern(iface); + return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_Select(ILegacyIAccessibleProvider *iface, LONG select_flags) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_DoDefaultAction(ILegacyIAccessibleProvider *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_SetValue(ILegacyIAccessibleProvider *iface, LPCWSTR val) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_GetIAccessible(ILegacyIAccessibleProvider *iface, + IAccessible **out_acc) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_ChildId(ILegacyIAccessibleProvider *iface, int *out_cid) +{ + struct Provider *Provider = impl_from_ProviderLegacyIAccessiblePattern(iface); + + *out_cid = Provider->legacy_acc_pattern_data.child_id; + return S_OK; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Name(ILegacyIAccessibleProvider *iface, BSTR *out_name) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Value(ILegacyIAccessibleProvider *iface, BSTR *out_value) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Description(ILegacyIAccessibleProvider *iface, + BSTR *out_description) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Help(ILegacyIAccessibleProvider *iface, BSTR *out_help) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_KeyboardShortcut(ILegacyIAccessibleProvider *iface, + BSTR *out_kbd_shortcut) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_GetSelection(ILegacyIAccessibleProvider *iface, + SAFEARRAY **out_selected) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_DefaultAction(ILegacyIAccessibleProvider *iface, + BSTR *out_default_action) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const ILegacyIAccessibleProviderVtbl ProviderLegacyIAccessiblePatternVtbl = { + ProviderLegacyIAccessiblePattern_QueryInterface, + ProviderLegacyIAccessiblePattern_AddRef, + ProviderLegacyIAccessiblePattern_Release, + ProviderLegacyIAccessiblePattern_Select, + ProviderLegacyIAccessiblePattern_DoDefaultAction, + ProviderLegacyIAccessiblePattern_SetValue, + ProviderLegacyIAccessiblePattern_GetIAccessible, + ProviderLegacyIAccessiblePattern_get_ChildId, + ProviderLegacyIAccessiblePattern_get_Name, + ProviderLegacyIAccessiblePattern_get_Value, + ProviderLegacyIAccessiblePattern_get_Description, + ProviderLegacyIAccessiblePattern_get_Role, + ProviderLegacyIAccessiblePattern_get_State, + ProviderLegacyIAccessiblePattern_get_Help, + ProviderLegacyIAccessiblePattern_get_KeyboardShortcut, + ProviderLegacyIAccessiblePattern_GetSelection, + ProviderLegacyIAccessiblePattern_get_DefaultAction, +}; + static struct Provider Provider = { { &ProviderSimpleVtbl }, @@ -2183,6 +2333,7 @@ static struct Provider Provider = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider", NULL, NULL, @@ -2198,6 +2349,7 @@ static struct Provider Provider2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider2", NULL, NULL, @@ -2213,6 +2365,7 @@ static struct Provider Provider_child = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_child", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, @@ -2228,6 +2381,7 @@ static struct Provider Provider_child2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_child2", &Provider.IRawElementProviderFragment_iface, &Provider.IRawElementProviderFragmentRoot_iface, @@ -2243,6 +2397,7 @@ static struct Provider Provider_hwnd = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_hwnd", NULL, NULL, @@ -2258,6 +2413,7 @@ static struct Provider Provider_nc = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_nc", NULL, NULL, @@ -2274,6 +2430,7 @@ static struct Provider Provider_proxy = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_proxy", NULL, NULL, @@ -2290,6 +2447,7 @@ static struct Provider Provider_proxy2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_proxy2", NULL, NULL, @@ -2306,6 +2464,7 @@ static struct Provider Provider_override = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderValuePatternVtbl }, + { &ProviderLegacyIAccessiblePatternVtbl }, 1, "Provider_override", NULL, NULL, @@ -2323,6 +2482,7 @@ static struct Provider Provider_override = { &ProviderFragmentRootVtbl }, \ { &ProviderHwndOverrideVtbl }, \ { &ProviderValuePatternVtbl }, \ + { &ProviderLegacyIAccessiblePatternVtbl }, \ 1, \ "Provider_" # name "", \ NULL, NULL, \ @@ -5065,6 +5225,12 @@ static const struct prov_method_sequence get_pattern_prop_seq[] = { { 0 } };
+static const struct prov_method_sequence get_pattern_prop_seq2[] = { + { &Provider, PROV_GET_PATTERN_PROV }, + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { 0 } +}; + static const struct prov_method_sequence get_bounding_rect_seq[] = { NODE_CREATE_SEQ(&Provider_child), { &Provider_child, FRAG_GET_BOUNDING_RECT }, @@ -5520,6 +5686,28 @@ static void test_UiaGetPropertyValue(void) VariantClear(&v); }
+ /* ILegacyIAccessibleProvider pattern property IDs. */ + Provider.legacy_acc_pattern_data.is_supported = FALSE; + hr = UiaGetPropertyValue(node, UIA_LegacyIAccessibleChildIdPropertyId, &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_seq2, NULL); + VariantClear(&v); + + Provider.legacy_acc_pattern_data.is_supported = TRUE; + for (i = 0; i < 2; i++) + { + Provider.legacy_acc_pattern_data.child_id = i; + + hr = UiaGetPropertyValue(node, UIA_LegacyIAccessibleChildIdPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == i, "Unexpected I4 %#lx\n", V_I4(&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); @@ -9013,6 +9201,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, 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)); + memset(&prov->legacy_acc_pattern_data, 0, sizeof(prov->legacy_acc_pattern_data)); if (initialize_nav_links) { prov->frag_root = NULL; diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index dae78ea8ff4..95287179f92 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1571,6 +1571,16 @@ static HRESULT uia_provider_get_pattern_prop_val(struct uia_provider *prov, break; }
+ case UIA_LegacyIAccessibleChildIdPropertyId: + { + int val; + + hr = ILegacyIAccessibleProvider_get_ChildId((ILegacyIAccessibleProvider *)pattern_prov, &val); + if (SUCCEEDED(hr)) + variant_init_i4(ret_val, val); + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index d518b2dd44f..62f26de0982 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -152,7 +152,9 @@ static const struct uia_prop_info default_uia_properties[] = { { &RangeValue_SmallChange_Property_GUID, UIA_RangeValueSmallChangePropertyId, }, { &IsTextEditPatternAvailable_Property_GUID, UIA_IsTextEditPatternAvailablePropertyId, }, { &GridItem_Column_Property_GUID, UIA_GridItemColumnPropertyId, }, - { &LegacyIAccessible_ChildId_Property_GUID, UIA_LegacyIAccessibleChildIdPropertyId, }, + { &LegacyIAccessible_ChildId_Property_GUID, UIA_LegacyIAccessibleChildIdPropertyId, + PROP_TYPE_PATTERN_PROP, UIAutomationType_Int, + UIA_LegacyIAccessiblePatternId, }, { &Annotation_DateTime_Property_GUID, UIA_AnnotationDateTimePropertyId, }, { &IsTablePatternAvailable_Property_GUID, UIA_IsTablePatternAvailablePropertyId, }, { &SelectionItem_IsSelected_Property_GUID, UIA_SelectionItemIsSelectedPropertyId, }, diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index f7e991ee4fd..63c6f7dde22 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -99,6 +99,12 @@ static inline void variant_init_bool(VARIANT *v, BOOL val) V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; }
+static inline void variant_init_i4(VARIANT *v, int val) +{ + V_VT(v) = VT_I4; + V_I4(v) = val; +} + static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) { SIZE_T max_capacity, new_capacity; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 119a7e69abc..a950df599d8 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -28,12 +28,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
DEFINE_GUID(SID_AccFromDAWrapper, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
-static void variant_init_i4(VARIANT *v, int val) -{ - V_VT(v) = VT_I4; - V_I4(v) = val; -} - 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 | 19 +++++++++++++++++-- dlls/uiautomationcore/uia_client.c | 10 ++++++++++ dlls/uiautomationcore/uia_ids.c | 4 +++- 3 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 8f28271b25b..2598fd795fe 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1119,6 +1119,7 @@ struct Provider_legacy_accessible_pattern_data { BOOL is_supported; int child_id; + DWORD role; };
static struct Provider @@ -2269,8 +2270,10 @@ static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Description(ILegacyIA
static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + struct Provider *Provider = impl_from_ProviderLegacyIAccessiblePattern(iface); + + *out_role = Provider->legacy_acc_pattern_data.role; + return S_OK; }
static HRESULT WINAPI ProviderLegacyIAccessiblePattern_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state) @@ -5708,6 +5711,18 @@ static void test_UiaGetPropertyValue(void) VariantClear(&v); }
+ for (i = 0; i < 2; i++) + { + Provider.legacy_acc_pattern_data.role = i; + + hr = UiaGetPropertyValue(node, UIA_LegacyIAccessibleRolePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == i, "Unexpected I4 %#lx\n", V_I4(&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); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 95287179f92..92ff41e2c71 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1581,6 +1581,16 @@ static HRESULT uia_provider_get_pattern_prop_val(struct uia_provider *prov, break; }
+ case UIA_LegacyIAccessibleRolePropertyId: + { + DWORD val; + + hr = ILegacyIAccessibleProvider_get_Role((ILegacyIAccessibleProvider *)pattern_prov, &val); + if (SUCCEEDED(hr)) + variant_init_i4(ret_val, val); + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index 62f26de0982..d23ba14c333 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -219,7 +219,9 @@ static const struct uia_prop_info default_uia_properties[] = { { &IsDialog_Property_GUID, UIA_IsDialogPropertyId, PROP_TYPE_ELEM_PROP, UIAutomationType_Bool, }, { &IsTextPatternAvailable_Property_GUID, UIA_IsTextPatternAvailablePropertyId, }, - { &LegacyIAccessible_Role_Property_GUID, UIA_LegacyIAccessibleRolePropertyId, }, + { &LegacyIAccessible_Role_Property_GUID, UIA_LegacyIAccessibleRolePropertyId, + PROP_TYPE_PATTERN_PROP, UIAutomationType_Int, + UIA_LegacyIAccessiblePatternId, }, { &Selection2_ItemCount_Property_GUID, UIA_Selection2ItemCountPropertyId, }, { &TableItem_RowHeaderItems_Property_GUID, UIA_TableItemRowHeaderItemsPropertyId, }, { &Styles_ExtendedProperties_Property_GUID, UIA_StylesExtendedPropertiesPropertyId, },
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 19 +++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 15 +++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 2598fd795fe..8741e565bb6 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -3556,6 +3556,9 @@ static void test_uia_prov_from_acc_properties(void) for (i = 0; i < ARRAY_SIZE(msaa_role_uia_types); i++) { const struct msaa_role_uia_type *role = &msaa_role_uia_types[i]; + ILegacyIAccessibleProvider *accprov; + DWORD role_val; + IUnknown *unk;
/* * Roles get cached once a valid one is mapped, so create a new @@ -3588,6 +3591,22 @@ static void test_uia_prov_from_acc_properties(void) if (!role->uia_control_type) CHECK_CALLED(Accessible_get_accRole);
+ hr = IRawElementProviderSimple_GetPatternProvider(elprov, UIA_LegacyIAccessiblePatternId, &unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!unk, "unk == NULL\n"); + + hr = IUnknown_QueryInterface(unk, &IID_ILegacyIAccessibleProvider, (void **)&accprov); + IUnknown_Release(unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!accprov, "accprov == NULL\n"); + + SET_EXPECT(Accessible_get_accRole); + hr = ILegacyIAccessibleProvider_get_Role(accprov, &role_val); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(role_val == Accessible.role, "role_val != Accessible.role\n"); + CHECK_CALLED(Accessible_get_accRole); + + ILegacyIAccessibleProvider_Release(accprov); IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); } diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index a950df599d8..59be5d3782a 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1066,8 +1066,19 @@ static HRESULT WINAPI msaa_acc_provider_get_Description(ILegacyIAccessibleProvid
static HRESULT WINAPI msaa_acc_provider_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role) { - FIXME("%p, %p: stub!\n", iface, out_role); - return E_NOTIMPL; + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + HRESULT hr; + VARIANT v; + + TRACE("%p, %p\n", iface, out_role); + + *out_role = 0; + VariantInit(&v); + hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v); + if (SUCCEEDED(hr) && V_VT(&v) == VT_I4) + *out_role = V_I4(&v); + + return S_OK; }
static HRESULT WINAPI msaa_acc_provider_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state)
V2: Remove tests for `UIA_IsOffscreenPropertyId` post HWND destruction.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/tests/uiautomation.c:
- /* Accessible now outside of its window's bounds. */
- set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].right, rect[0].bottom,
10, 10);
- SET_EXPECT(Accessible_get_accState);
- SET_EXPECT(Accessible_accLocation);
- hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v));
- ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v));
- CHECK_CALLED(Accessible_get_accState);
- CHECK_CALLED(Accessible_accLocation);
- /* Accessible within window bounds, but not client area bounds. */
- ok(GetWindowRect(Accessible.ow_hwnd, &rect[1]), "GetClientRect returned FALSE\n");
- set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[1].left, rect[1].top,
rect[0].left - 1, rect[0].top - 1);
This seems to be assuming the last 2 arguments are right/bottom rather than width/height.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/tests/uiautomation.c:
- SET_EXPECT(Accessible_get_accState);
- SET_EXPECT(Accessible_accLocation);
- hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v));
- ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v));
- CHECK_CALLED(Accessible_get_accState);
- CHECK_CALLED(Accessible_accLocation);
- /* Window is visible, Accessible is within its bounds. */
- ShowWindow(Accessible.ow_hwnd, SW_SHOW);
- ok(GetClientRect(Accessible.ow_hwnd, &rect[0]), "GetClientRect returned FALSE\n");
- MapWindowPoints(Accessible.ow_hwnd, NULL, (POINT *)&rect[0], 2);
- set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].left, rect[0].top,
(rect[0].right - rect[0].left), (rect[0].bottom - rect[0].top));
The documentation for GetClientRect says that it returns client coordinates (and that the upper-left corner is always 0,0), but the documentation for accLocation says that it returns screen coordinates.
On Fri Mar 31 18:03:22 2023 +0000, Esme Povirk wrote:
This seems to be assuming the last 2 arguments are right/bottom rather than width/height.
The message on line 3549 also has the wrong function name.
On Fri Mar 31 18:08:48 2023 +0000, Esme Povirk wrote:
The documentation for GetClientRect says that it returns client coordinates (and that the upper-left corner is always 0,0), but the documentation for accLocation says that it returns screen coordinates.
IIUC, the call to `MapWindowPoints()` here should convert from the client coordinates of the `hWndFrom` argument (our HWND) to the coordinates of the `hWndTo` argument (NULL here, which means desktop) which converts the coordinates to screen coordinates.
On Fri Mar 31 18:46:43 2023 +0000, Connor McAdams wrote:
IIUC, the call to `MapWindowPoints()` here should convert from the client coordinates of the `hWndFrom` argument (our HWND) to the coordinates of the `hWndTo` argument (NULL here, which means desktop) which converts the coordinates to screen coordinates.
Oh, you're right, sorry.
On Fri Mar 31 18:09:32 2023 +0000, Esme Povirk wrote:
The message on line 3549 also has the wrong function name.
These coordinates were based on the fact/assumption that when we create our HWND it's at position `0,0` on the screen. On my Win10 machine:
- rect[0] is (8,31)-(128,92) (Window client area) - rect[1] is (0,0)-(136,100) (Entire window including non-client area rectangle)
So this should set width to 7, and height to 30, which should be within the bounding box of the non-client area and not the client area. I guess we should probably adjust them based on the coordinates of the entire window. Or I guess just a static width/height of 1 might do just as well...
On Fri Mar 31 18:58:28 2023 +0000, Connor McAdams wrote:
These coordinates were based on the fact/assumption that when we create our HWND it's at position `0,0` on the screen. On my Win10 machine:
- rect[0] is (8,31)-(128,92) (Window client area)
- rect[1] is (0,0)-(136,100) (Entire window including non-client area rectangle)
So this should set width to 7, and height to 30, which should be within the bounding box of the non-client area and not the client area. I guess we should probably adjust them based on the coordinates of the entire window. Or I guess just a static width/height of 1 might do just as well...
It passes in rect[1] left/top, instead of hard-coding them, so that's an inconsistency where it assumes 0,0 in some places but not others.
Given that the window isn't shown I guess it probably holds for now, but that may be inconvenient if there's a need to show the window later. I think it'd be cleaner to not make the assumption.