Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 170 ++++++++++++++++++++- dlls/uiautomationcore/uia_provider.c | 100 +++++++++++- 2 files changed, 266 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f61a4b21c50..c735e7a0bd5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -58,6 +58,7 @@ static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, DEFINE_EXPECT(winproc_GETOBJECT_CLIENT); DEFINE_EXPECT(Accessible_accNavigate); DEFINE_EXPECT(Accessible_get_accParent); +DEFINE_EXPECT(Accessible_get_accRole); DEFINE_EXPECT(Accessible_child_accNavigate); DEFINE_EXPECT(Accessible_child_get_accParent);
@@ -80,6 +81,7 @@ static struct Accessible IAccessible *parent; HWND acc_hwnd; HWND ow_hwnd; + INT role; } Accessible, Accessible_child;
static inline struct Accessible* impl_from_Accessible(IAccessible *iface) @@ -197,7 +199,18 @@ static HRESULT WINAPI Accessible_get_accDescription(IAccessible *iface, VARIANT static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_id, VARIANT *out_role) { - ok(0, "unexpected call\n"); + struct Accessible *This = impl_from_Accessible(iface); + + ok(This == &Accessible, "unexpected call\n"); + CHECK_EXPECT(Accessible_get_accRole); + + if (This->role) + { + V_VT(out_role) = VT_I4; + V_I4(out_role) = This->role; + return S_OK; + } + return E_NOTIMPL; }
@@ -395,7 +408,8 @@ static struct Accessible Accessible = { &OleWindowVtbl }, 1, NULL, - 0, 0 + 0, 0, + 0, }; static struct Accessible Accessible_child = { @@ -403,7 +417,8 @@ static struct Accessible Accessible_child = { &OleWindowVtbl }, 1, &Accessible.IAccessible_iface, - 0, 0 + 0, 0, + 0, };
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -662,6 +677,153 @@ static void test_uia_reserved_value_ifaces(void) CoUninitialize(); }
+struct msaa_role_uia_type { + INT acc_role; + INT uia_control_type; +}; + +static const struct msaa_role_uia_type msaa_role_uia_types[] = { + { ROLE_SYSTEM_TITLEBAR, UIA_TitleBarControlTypeId }, + { ROLE_SYSTEM_MENUBAR, UIA_MenuBarControlTypeId }, + { ROLE_SYSTEM_SCROLLBAR, UIA_ScrollBarControlTypeId }, + { ROLE_SYSTEM_GRIP, UIA_ThumbControlTypeId }, + { ROLE_SYSTEM_WINDOW, UIA_WindowControlTypeId }, + { ROLE_SYSTEM_MENUPOPUP, UIA_MenuControlTypeId }, + { ROLE_SYSTEM_MENUITEM, UIA_MenuItemControlTypeId }, + { ROLE_SYSTEM_TOOLTIP, UIA_ToolTipControlTypeId }, + { ROLE_SYSTEM_APPLICATION, UIA_WindowControlTypeId }, + { ROLE_SYSTEM_DOCUMENT, UIA_DocumentControlTypeId }, + { ROLE_SYSTEM_PANE, UIA_PaneControlTypeId }, + { ROLE_SYSTEM_GROUPING, UIA_GroupControlTypeId }, + { ROLE_SYSTEM_SEPARATOR, UIA_SeparatorControlTypeId }, + { ROLE_SYSTEM_TOOLBAR, UIA_ToolBarControlTypeId }, + { ROLE_SYSTEM_STATUSBAR, UIA_StatusBarControlTypeId }, + { ROLE_SYSTEM_TABLE, UIA_TableControlTypeId }, + { ROLE_SYSTEM_COLUMNHEADER, UIA_HeaderControlTypeId }, + { ROLE_SYSTEM_ROWHEADER, UIA_HeaderControlTypeId }, + { ROLE_SYSTEM_CELL, UIA_DataItemControlTypeId }, + { ROLE_SYSTEM_LINK, UIA_HyperlinkControlTypeId }, + { ROLE_SYSTEM_LIST, UIA_ListControlTypeId }, + { ROLE_SYSTEM_LISTITEM, UIA_ListItemControlTypeId }, + { ROLE_SYSTEM_OUTLINE, UIA_TreeControlTypeId }, + { ROLE_SYSTEM_OUTLINEITEM, UIA_TreeItemControlTypeId }, + { ROLE_SYSTEM_PAGETAB, UIA_TabItemControlTypeId }, + { ROLE_SYSTEM_INDICATOR, UIA_ThumbControlTypeId }, + { ROLE_SYSTEM_GRAPHIC, UIA_ImageControlTypeId }, + { ROLE_SYSTEM_STATICTEXT, UIA_TextControlTypeId }, + { ROLE_SYSTEM_TEXT, UIA_EditControlTypeId }, + { ROLE_SYSTEM_PUSHBUTTON, UIA_ButtonControlTypeId }, + { ROLE_SYSTEM_CHECKBUTTON, UIA_CheckBoxControlTypeId }, + { ROLE_SYSTEM_RADIOBUTTON, UIA_RadioButtonControlTypeId }, + { ROLE_SYSTEM_COMBOBOX, UIA_ComboBoxControlTypeId }, + { ROLE_SYSTEM_PROGRESSBAR, UIA_ProgressBarControlTypeId }, + { ROLE_SYSTEM_SLIDER, UIA_SliderControlTypeId }, + { ROLE_SYSTEM_SPINBUTTON, UIA_SpinnerControlTypeId }, + { ROLE_SYSTEM_BUTTONDROPDOWN, UIA_SplitButtonControlTypeId }, + { ROLE_SYSTEM_BUTTONMENU, UIA_MenuItemControlTypeId }, + { ROLE_SYSTEM_BUTTONDROPDOWNGRID, UIA_ButtonControlTypeId }, + { ROLE_SYSTEM_PAGETABLIST, UIA_TabControlTypeId }, + { ROLE_SYSTEM_SPLITBUTTON, UIA_SplitButtonControlTypeId }, + /* These accessible roles have no equivalent in UI Automation. */ + { ROLE_SYSTEM_SOUND, 0 }, + { ROLE_SYSTEM_CURSOR, 0 }, + { ROLE_SYSTEM_CARET, 0 }, + { ROLE_SYSTEM_ALERT, 0 }, + { ROLE_SYSTEM_CLIENT, 0 }, + { ROLE_SYSTEM_CHART, 0 }, + { ROLE_SYSTEM_DIALOG, 0 }, + { ROLE_SYSTEM_BORDER, 0 }, + { ROLE_SYSTEM_COLUMN, 0 }, + { ROLE_SYSTEM_ROW, 0 }, + { ROLE_SYSTEM_HELPBALLOON, 0 }, + { ROLE_SYSTEM_CHARACTER, 0 }, + { ROLE_SYSTEM_PROPERTYPAGE, 0 }, + { ROLE_SYSTEM_DROPLIST, 0 }, + { ROLE_SYSTEM_DIAL, 0 }, + { ROLE_SYSTEM_HOTKEYFIELD, 0 }, + { ROLE_SYSTEM_DIAGRAM, 0 }, + { ROLE_SYSTEM_ANIMATION, 0 }, + { ROLE_SYSTEM_EQUATION, 0 }, + { ROLE_SYSTEM_WHITESPACE, 0 }, + { ROLE_SYSTEM_IPADDRESS, 0 }, + { ROLE_SYSTEM_OUTLINEBUTTON, 0 }, +}; + +static void test_uia_prov_from_acc_properties(void) +{ + IRawElementProviderSimple *elprov; + HRESULT hr; + VARIANT v; + int i; + + /* MSAA role to UIA control type test. */ + for (i = 0; i < ARRAY_SIZE(msaa_role_uia_types); i++) + { + const struct msaa_role_uia_type *role = &msaa_role_uia_types[i]; + + /* + * Roles get cached once a valid one is mapped, so create a new + * element for each role. + */ + 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); + + Accessible.role = role->acc_role; + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (role->uia_control_type) + ok(check_variant_i4(&v, role->uia_control_type), "MSAA role %d: V_I4(&v) = %ld\n", role->acc_role, V_I4(&v)); + else + ok(V_VT(&v) == VT_EMPTY, "MSAA role %d: V_VT(&v) = %d\n", role->acc_role, V_VT(&v)); + CHECK_CALLED(Accessible_get_accRole); + + if (!role->uia_control_type) + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (role->uia_control_type) + ok(check_variant_i4(&v, role->uia_control_type), "MSAA role %d: V_I4(&v) = %ld\n", role->acc_role, V_I4(&v)); + else + ok(V_VT(&v) == VT_EMPTY, "MSAA role %d: V_VT(&v) = %d\n", role->acc_role, V_VT(&v)); + if (!role->uia_control_type) + CHECK_CALLED(Accessible_get_accRole); + + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + } + + /* ROLE_SYSTEM_CLOCK has no mapping in Windows < 10 1809. */ + 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); + + Accessible.role = ROLE_SYSTEM_CLOCK; + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_i4(&v, UIA_ButtonControlTypeId) || broken(V_VT(&v) == VT_EMPTY), /* Windows < 10 1809 */ + "MSAA role %d: V_I4(&v) = %ld\n", Accessible.role, V_I4(&v)); + CHECK_CALLED(Accessible_get_accRole); + + if (V_VT(&v) == VT_EMPTY) + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(check_variant_i4(&v, UIA_ButtonControlTypeId) || broken(V_VT(&v) == VT_EMPTY), /* Windows < 10 1809 */ + "MSAA role %d: V_I4(&v) = %ld\n", Accessible.role, V_I4(&v)); + if (V_VT(&v) == VT_EMPTY) + CHECK_CALLED(Accessible_get_accRole); + + Accessible.role = 0; + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); +} + static void test_UiaProviderFromIAccessible(void) { IRawElementProviderSimple *elprov; @@ -781,6 +943,8 @@ static void test_UiaProviderFromIAccessible(void) IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+ test_uia_prov_from_acc_properties(); + DestroyWindow(hwnd); UnregisterClassA("pUiaProviderFromIAccessible class", NULL); Accessible.acc_hwnd = NULL; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index d2a0da15c3c..ab2b8305b10 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -32,6 +32,85 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static LONG msaa_role_to_uia_control_type(LONG role) +{ + switch (role) + { + case ROLE_SYSTEM_TITLEBAR: return UIA_TitleBarControlTypeId; + case ROLE_SYSTEM_MENUBAR: return UIA_MenuBarControlTypeId; + case ROLE_SYSTEM_SCROLLBAR: return UIA_ScrollBarControlTypeId; + case ROLE_SYSTEM_INDICATOR: + case ROLE_SYSTEM_GRIP: return UIA_ThumbControlTypeId; + case ROLE_SYSTEM_APPLICATION: + case ROLE_SYSTEM_WINDOW: return UIA_WindowControlTypeId; + case ROLE_SYSTEM_MENUPOPUP: return UIA_MenuControlTypeId; + case ROLE_SYSTEM_TOOLTIP: return UIA_ToolTipControlTypeId; + case ROLE_SYSTEM_DOCUMENT: return UIA_DocumentControlTypeId; + case ROLE_SYSTEM_PANE: return UIA_PaneControlTypeId; + case ROLE_SYSTEM_GROUPING: return UIA_GroupControlTypeId; + case ROLE_SYSTEM_SEPARATOR: return UIA_SeparatorControlTypeId; + case ROLE_SYSTEM_TOOLBAR: return UIA_ToolBarControlTypeId; + case ROLE_SYSTEM_STATUSBAR: return UIA_StatusBarControlTypeId; + case ROLE_SYSTEM_TABLE: return UIA_TableControlTypeId; + case ROLE_SYSTEM_COLUMNHEADER: + case ROLE_SYSTEM_ROWHEADER: return UIA_HeaderControlTypeId; + case ROLE_SYSTEM_CELL: return UIA_DataItemControlTypeId; + case ROLE_SYSTEM_LINK: return UIA_HyperlinkControlTypeId; + case ROLE_SYSTEM_LIST: return UIA_ListControlTypeId; + case ROLE_SYSTEM_LISTITEM: return UIA_ListItemControlTypeId; + case ROLE_SYSTEM_OUTLINE: return UIA_TreeControlTypeId; + case ROLE_SYSTEM_OUTLINEITEM: return UIA_TreeItemControlTypeId; + case ROLE_SYSTEM_PAGETAB: return UIA_TabItemControlTypeId; + case ROLE_SYSTEM_GRAPHIC: return UIA_ImageControlTypeId; + case ROLE_SYSTEM_STATICTEXT: return UIA_TextControlTypeId; + case ROLE_SYSTEM_TEXT: return UIA_EditControlTypeId; + case ROLE_SYSTEM_CLOCK: + case ROLE_SYSTEM_BUTTONDROPDOWNGRID: + case ROLE_SYSTEM_PUSHBUTTON: return UIA_ButtonControlTypeId; + case ROLE_SYSTEM_CHECKBUTTON: return UIA_CheckBoxControlTypeId; + case ROLE_SYSTEM_RADIOBUTTON: return UIA_RadioButtonControlTypeId; + case ROLE_SYSTEM_COMBOBOX: return UIA_ComboBoxControlTypeId; + case ROLE_SYSTEM_PROGRESSBAR: return UIA_ProgressBarControlTypeId; + case ROLE_SYSTEM_SLIDER: return UIA_SliderControlTypeId; + case ROLE_SYSTEM_SPINBUTTON: return UIA_SpinnerControlTypeId; + case ROLE_SYSTEM_BUTTONMENU: + case ROLE_SYSTEM_MENUITEM: return UIA_MenuItemControlTypeId; + case ROLE_SYSTEM_PAGETABLIST: return UIA_TabControlTypeId; + case ROLE_SYSTEM_BUTTONDROPDOWN: + case ROLE_SYSTEM_SPLITBUTTON: return UIA_SplitButtonControlTypeId; + case ROLE_SYSTEM_SOUND: + case ROLE_SYSTEM_CURSOR: + case ROLE_SYSTEM_CARET: + case ROLE_SYSTEM_ALERT: + case ROLE_SYSTEM_CLIENT: + case ROLE_SYSTEM_CHART: + case ROLE_SYSTEM_DIALOG: + case ROLE_SYSTEM_BORDER: + case ROLE_SYSTEM_COLUMN: + case ROLE_SYSTEM_ROW: + case ROLE_SYSTEM_HELPBALLOON: + case ROLE_SYSTEM_CHARACTER: + case ROLE_SYSTEM_PROPERTYPAGE: + case ROLE_SYSTEM_DROPLIST: + case ROLE_SYSTEM_DIAL: + case ROLE_SYSTEM_HOTKEYFIELD: + case ROLE_SYSTEM_DIAGRAM: + case ROLE_SYSTEM_ANIMATION: + case ROLE_SYSTEM_EQUATION: + case ROLE_SYSTEM_WHITESPACE: + case ROLE_SYSTEM_IPADDRESS: + case ROLE_SYSTEM_OUTLINEBUTTON: + WARN("No UIA control type mapping for MSAA role %ld\n", role); + break; + + default: + FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role); + break; + } + + return 0; +} + /* * UiaProviderFromIAccessible IRawElementProviderSimple interface. */ @@ -42,6 +121,7 @@ struct msaa_provider { IAccessible *acc; VARIANT cid; HWND hwnd; + LONG control_type; };
static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface) @@ -106,9 +186,14 @@ HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, PROPERTYID prop_id, VARIANT *ret_val) { + struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface); + HRESULT hr; + VARIANT v; + TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
VariantInit(ret_val); + VariantInit(&v); switch (prop_id) { case UIA_ProviderDescriptionPropertyId: @@ -116,6 +201,19 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, V_BSTR(ret_val) = SysAllocString(L"Wine: MSAA Proxy"); break;
+ case UIA_ControlTypePropertyId: + if (!msaa_prov->control_type) + { + hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v); + if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4)) + msaa_prov->control_type = msaa_role_to_uia_control_type(V_I4(&v)); + } + + if (msaa_prov->control_type) + variant_init_i4(ret_val, msaa_prov->control_type); + + break; + default: FIXME("Unimplemented propertyId %d\n", prop_id); break; @@ -192,7 +290,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD if (!hwnd) return E_FAIL;
- msaa_prov = heap_alloc(sizeof(*msaa_prov)); + msaa_prov = heap_alloc_zero(sizeof(*msaa_prov)); if (!msaa_prov) return E_OUTOFMEMORY;