[PATCH 5/6] uiautomationcore: Implement UIA_ControlTypePropertyId for MSAA providers.

Connor McAdams wine at gitlab.winehq.org
Wed Jun 8 08:53:18 CDT 2022


From: Connor McAdams <cmcadams at codeweavers.com>

Signed-off-by: Connor McAdams <cmcadams at 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;
 
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/207



More information about the wine-devel mailing list