[PATCH v3 3/3] uiautomationcore: Add basic IAccessible2 support.

Connor McAdams wine at gitlab.winehq.org
Wed Jun 15 09:39:58 CDT 2022


From: Connor McAdams <cmcadams at codeweavers.com>

Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
 dlls/uiautomationcore/tests/uiautomation.c | 603 +++++++++++++++++++++
 dlls/uiautomationcore/uia_provider.c       |  53 ++
 2 files changed, 656 insertions(+)

diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 15d7b4e784b..298a3942cbb 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -24,6 +24,7 @@
 #include "initguid.h"
 #include "uiautomation.h"
 #include "ocidl.h"
+#include "wine/iaccessible2.h"
 
 #include "wine/test.h"
 
@@ -73,6 +74,7 @@ DEFINE_EXPECT(Accessible_get_accRole);
 DEFINE_EXPECT(Accessible_get_accState);
 DEFINE_EXPECT(Accessible_accLocation);
 DEFINE_EXPECT(Accessible_get_accChild);
+DEFINE_EXPECT(Accessible_get_uniqueID);
 DEFINE_EXPECT(Accessible2_get_accParent);
 DEFINE_EXPECT(Accessible2_get_accChildCount);
 DEFINE_EXPECT(Accessible2_get_accName);
@@ -80,6 +82,7 @@ DEFINE_EXPECT(Accessible2_get_accRole);
 DEFINE_EXPECT(Accessible2_get_accState);
 DEFINE_EXPECT(Accessible2_accLocation);
 DEFINE_EXPECT(Accessible2_QI_IAccIdentity);
+DEFINE_EXPECT(Accessible2_get_uniqueID);
 DEFINE_EXPECT(Accessible_child_accNavigate);
 DEFINE_EXPECT(Accessible_child_get_accParent);
 DEFINE_EXPECT(Accessible_child_get_accChildCount);
@@ -128,7 +131,9 @@ static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2)
 static struct Accessible
 {
     IAccessible IAccessible_iface;
+    IAccessible2 IAccessible2_iface;
     IOleWindow IOleWindow_iface;
+    IServiceProvider IServiceProvider_iface;
     LONG ref;
 
     IAccessible *parent;
@@ -139,6 +144,8 @@ static struct Accessible
     LONG child_count;
     LPCWSTR name;
     LONG left, top, width, height;
+    BOOL enable_ia2;
+    LONG unique_id;
 } Accessible, Accessible2, Accessible_child, Accessible_child2;
 
 static inline struct Accessible* impl_from_Accessible(IAccessible *iface)
@@ -164,6 +171,10 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid,
         *obj = iface;
     else if (IsEqualIID(riid, &IID_IOleWindow))
         *obj = &This->IOleWindow_iface;
+    else if (IsEqualIID(riid, &IID_IServiceProvider))
+        *obj = &This->IServiceProvider_iface;
+    else if (IsEqualIID(riid, &IID_IAccessible2) && This->enable_ia2)
+        *obj = &This->IAccessible2_iface;
     else
         return E_NOINTERFACE;
 
@@ -559,6 +570,383 @@ static IAccessibleVtbl AccessibleVtbl = {
     Accessible_put_accValue
 };
 
+static inline struct Accessible* impl_from_Accessible2(IAccessible2 *iface)
+{
+    return CONTAINING_RECORD(iface, struct Accessible, IAccessible2_iface);
+}
+
+static HRESULT WINAPI Accessible2_QueryInterface(IAccessible2 *iface, REFIID riid, void **obj)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_QueryInterface(&This->IAccessible_iface, riid, obj);
+}
+
+static ULONG WINAPI Accessible2_AddRef(IAccessible2 *iface)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_AddRef(&This->IAccessible_iface);
+}
+
+static ULONG WINAPI Accessible2_Release(IAccessible2 *iface)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_Release(&This->IAccessible_iface);
+}
+
+static HRESULT WINAPI Accessible2_GetTypeInfoCount(IAccessible2 *iface, UINT *pctinfo)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_GetTypeInfoCount(&This->IAccessible_iface, pctinfo);
+}
+
+static HRESULT WINAPI Accessible2_GetTypeInfo(IAccessible2 *iface, UINT iTInfo,
+        LCID lcid, ITypeInfo **out_tinfo)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_GetTypeInfo(&This->IAccessible_iface, iTInfo, lcid, out_tinfo);
+}
+
+static HRESULT WINAPI Accessible2_GetIDsOfNames(IAccessible2 *iface, REFIID riid,
+        LPOLESTR *rg_names, UINT name_count, LCID lcid, DISPID *rg_disp_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_GetIDsOfNames(&This->IAccessible_iface, riid, rg_names, name_count,
+            lcid, rg_disp_id);
+}
+
+static HRESULT WINAPI Accessible2_Invoke(IAccessible2 *iface, DISPID disp_id_member,
+        REFIID riid, LCID lcid, WORD flags, DISPPARAMS *disp_params,
+        VARIANT *var_result, EXCEPINFO *excep_info, UINT *arg_err)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_Invoke(&This->IAccessible_iface, disp_id_member, riid, lcid, flags,
+            disp_params, var_result, excep_info, arg_err);
+}
+
+static HRESULT WINAPI Accessible2_get_accParent(IAccessible2 *iface, IDispatch **out_parent)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accParent(&This->IAccessible_iface, out_parent);
+}
+
+static HRESULT WINAPI Accessible2_get_accChildCount(IAccessible2 *iface, LONG *out_count)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accChildCount(&This->IAccessible_iface, out_count);
+}
+
+static HRESULT WINAPI Accessible2_get_accChild(IAccessible2 *iface, VARIANT child_id,
+        IDispatch **out_child)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accChild(&This->IAccessible_iface, child_id, out_child);
+}
+
+static HRESULT WINAPI Accessible2_get_accName(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_name)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accName(&This->IAccessible_iface, child_id, out_name);
+}
+
+static HRESULT WINAPI Accessible2_get_accValue(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_value)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accValue(&This->IAccessible_iface, child_id, out_value);
+}
+
+static HRESULT WINAPI Accessible2_get_accDescription(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_description)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accDescription(&This->IAccessible_iface, child_id, out_description);
+}
+
+static HRESULT WINAPI Accessible2_get_accRole(IAccessible2 *iface, VARIANT child_id,
+        VARIANT *out_role)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accRole(&This->IAccessible_iface, child_id, out_role);
+}
+
+static HRESULT WINAPI Accessible2_get_accState(IAccessible2 *iface, VARIANT child_id,
+        VARIANT *out_state)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accState(&This->IAccessible_iface, child_id, out_state);
+}
+
+static HRESULT WINAPI Accessible2_get_accHelp(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_help)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accHelp(&This->IAccessible_iface, child_id, out_help);
+}
+
+static HRESULT WINAPI Accessible2_get_accHelpTopic(IAccessible2 *iface,
+        BSTR *out_help_file, VARIANT child_id, LONG *out_topic_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accHelpTopic(&This->IAccessible_iface, out_help_file, child_id,
+            out_topic_id);
+}
+
+static HRESULT WINAPI Accessible2_get_accKeyboardShortcut(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_kbd_shortcut)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accKeyboardShortcut(&This->IAccessible_iface, child_id,
+            out_kbd_shortcut);
+}
+
+static HRESULT WINAPI Accessible2_get_accFocus(IAccessible2 *iface, VARIANT *pchild_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accFocus(&This->IAccessible_iface, pchild_id);
+}
+
+static HRESULT WINAPI Accessible2_get_accSelection(IAccessible2 *iface, VARIANT *out_selection)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accSelection(&This->IAccessible_iface, out_selection);
+}
+
+static HRESULT WINAPI Accessible2_get_accDefaultAction(IAccessible2 *iface, VARIANT child_id,
+        BSTR *out_default_action)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_get_accDefaultAction(&This->IAccessible_iface, child_id,
+            out_default_action);
+}
+
+static HRESULT WINAPI Accessible2_accSelect(IAccessible2 *iface, LONG select_flags,
+        VARIANT child_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_accSelect(&This->IAccessible_iface, select_flags, child_id);
+}
+
+static HRESULT WINAPI Accessible2_accLocation(IAccessible2 *iface, LONG *out_left,
+        LONG *out_top, LONG *out_width, LONG *out_height, VARIANT child_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_accLocation(&This->IAccessible_iface, out_left, out_top, out_width,
+            out_height, child_id);
+}
+
+static HRESULT WINAPI Accessible2_accNavigate(IAccessible2 *iface, LONG nav_direction,
+        VARIANT child_id_start, VARIANT *out_var)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_accNavigate(&This->IAccessible_iface, nav_direction, child_id_start,
+            out_var);
+}
+
+static HRESULT WINAPI Accessible2_accHitTest(IAccessible2 *iface, LONG left, LONG top,
+        VARIANT *out_child_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_accHitTest(&This->IAccessible_iface, left, top, out_child_id);
+}
+
+static HRESULT WINAPI Accessible2_accDoDefaultAction(IAccessible2 *iface, VARIANT child_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_accDoDefaultAction(&This->IAccessible_iface, child_id);
+}
+
+static HRESULT WINAPI Accessible2_put_accName(IAccessible2 *iface, VARIANT child_id,
+        BSTR name)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_put_accName(&This->IAccessible_iface, child_id, name);
+}
+
+static HRESULT WINAPI Accessible2_put_accValue(IAccessible2 *iface, VARIANT child_id,
+        BSTR value)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+    return IAccessible_put_accValue(&This->IAccessible_iface, child_id, value);
+}
+
+static HRESULT WINAPI Accessible2_get_nRelations(IAccessible2 *iface, LONG *out_nRelations)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_relation(IAccessible2 *iface, LONG relation_idx,
+        IAccessibleRelation **out_relation)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_relations(IAccessible2 *iface, LONG count,
+        IAccessibleRelation **out_relations, LONG *out_relation_count)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_role(IAccessible2 *iface, LONG *out_role)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_scrollTo(IAccessible2 *iface, enum IA2ScrollType scroll_type)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_scrollToPoint(IAccessible2 *iface,
+        enum IA2CoordinateType coordinate_type, LONG x, LONG y)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_groupPosition(IAccessible2 *iface, LONG *out_group_level,
+        LONG *out_similar_items_in_group, LONG *out_position_in_group)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_states(IAccessible2 *iface, AccessibleStates *out_states)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_extendedRole(IAccessible2 *iface, BSTR *out_extended_role)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_localizedExtendedRole(IAccessible2 *iface,
+        BSTR *out_localized_extended_role)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_nExtendedStates(IAccessible2 *iface, LONG *out_nExtendedStates)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_extendedStates(IAccessible2 *iface, LONG count,
+        BSTR **out_extended_states, LONG *out_extended_states_count)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_localizedExtendedStates(IAccessible2 *iface, LONG count,
+        BSTR **out_localized_extended_states, LONG *out_localized_extended_states_count)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_uniqueID(IAccessible2 *iface, LONG *out_unique_id)
+{
+    struct Accessible *This = impl_from_Accessible2(iface);
+
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_uniqueID);
+    else
+        CHECK_EXPECT(Accessible_get_uniqueID);
+
+    *out_unique_id = 0;
+    if (This->unique_id)
+    {
+        *out_unique_id = This->unique_id;
+        return S_OK;
+    }
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_windowHandle(IAccessible2 *iface, HWND *out_hwnd)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_indexInParent(IAccessible2 *iface, LONG *out_idx_in_parent)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_locale(IAccessible2 *iface, IA2Locale *out_locale)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible2_get_attributes(IAccessible2 *iface, BSTR *out_attributes)
+{
+    ok(0, "unexpected call\n");
+    return E_NOTIMPL;
+}
+
+static const IAccessible2Vtbl Accessible2Vtbl = {
+    Accessible2_QueryInterface,
+    Accessible2_AddRef,
+    Accessible2_Release,
+    Accessible2_GetTypeInfoCount,
+    Accessible2_GetTypeInfo,
+    Accessible2_GetIDsOfNames,
+    Accessible2_Invoke,
+    Accessible2_get_accParent,
+    Accessible2_get_accChildCount,
+    Accessible2_get_accChild,
+    Accessible2_get_accName,
+    Accessible2_get_accValue,
+    Accessible2_get_accDescription,
+    Accessible2_get_accRole,
+    Accessible2_get_accState,
+    Accessible2_get_accHelp,
+    Accessible2_get_accHelpTopic,
+    Accessible2_get_accKeyboardShortcut,
+    Accessible2_get_accFocus,
+    Accessible2_get_accSelection,
+    Accessible2_get_accDefaultAction,
+    Accessible2_accSelect,
+    Accessible2_accLocation,
+    Accessible2_accNavigate,
+    Accessible2_accHitTest,
+    Accessible2_accDoDefaultAction,
+    Accessible2_put_accName,
+    Accessible2_put_accValue,
+    Accessible2_get_nRelations,
+    Accessible2_get_relation,
+    Accessible2_get_relations,
+    Accessible2_role,
+    Accessible2_scrollTo,
+    Accessible2_scrollToPoint,
+    Accessible2_get_groupPosition,
+    Accessible2_get_states,
+    Accessible2_get_extendedRole,
+    Accessible2_get_localizedExtendedRole,
+    Accessible2_get_nExtendedStates,
+    Accessible2_get_extendedStates,
+    Accessible2_get_localizedExtendedStates,
+    Accessible2_get_uniqueID,
+    Accessible2_get_windowHandle,
+    Accessible2_get_indexInParent,
+    Accessible2_get_locale,
+    Accessible2_get_attributes,
+};
+
 static inline struct Accessible* impl_from_OleWindow(IOleWindow *iface)
 {
     return CONTAINING_RECORD(iface, struct Accessible, IOleWindow_iface);
@@ -603,48 +991,102 @@ static const IOleWindowVtbl OleWindowVtbl = {
     OleWindow_ContextSensitiveHelp
 };
 
+static inline struct Accessible* impl_from_ServiceProvider(IServiceProvider *iface)
+{
+    return CONTAINING_RECORD(iface, struct Accessible, IServiceProvider_iface);
+}
+
+static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **obj)
+{
+    struct Accessible *This = impl_from_ServiceProvider(iface);
+    return IAccessible_QueryInterface(&This->IAccessible_iface, riid, obj);
+}
+
+static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
+{
+    struct Accessible *This = impl_from_ServiceProvider(iface);
+    return IAccessible_AddRef(&This->IAccessible_iface);
+}
+
+static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
+{
+    struct Accessible *This = impl_from_ServiceProvider(iface);
+    return IAccessible_Release(&This->IAccessible_iface);
+}
+
+static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, REFGUID service_guid,
+        REFIID riid, void **obj)
+{
+    struct Accessible *This = impl_from_ServiceProvider(iface);
+
+    if (IsEqualIID(riid, &IID_IAccessible2) && IsEqualIID(service_guid, &IID_IAccessible2) &&
+            This->enable_ia2)
+        return IAccessible_QueryInterface(&This->IAccessible_iface, riid, obj);
+
+    return E_NOTIMPL;
+}
+
+static const IServiceProviderVtbl ServiceProviderVtbl = {
+    ServiceProvider_QueryInterface,
+    ServiceProvider_AddRef,
+    ServiceProvider_Release,
+    ServiceProvider_QueryService,
+};
+
 static struct Accessible Accessible =
 {
     { &AccessibleVtbl },
+    { &Accessible2Vtbl },
     { &OleWindowVtbl },
+    { &ServiceProviderVtbl },
     1,
     NULL,
     0, 0,
     0, 0, 0, NULL,
     0, 0, 0, 0,
+    FALSE, 0,
 };
 
 static struct Accessible Accessible2 =
 {
     { &AccessibleVtbl },
+    { &Accessible2Vtbl },
     { &OleWindowVtbl },
+    { &ServiceProviderVtbl },
     1,
     NULL,
     0, 0,
     0, 0, 0, NULL,
     0, 0, 0, 0,
+    FALSE, 0,
 };
 
 static struct Accessible Accessible_child =
 {
     { &AccessibleVtbl },
+    { &Accessible2Vtbl },
     { &OleWindowVtbl },
+    { &ServiceProviderVtbl },
     1,
     &Accessible.IAccessible_iface,
     0, 0,
     0, 0, 0, NULL,
     0, 0, 0, 0,
+    FALSE, 0,
 };
 
 static struct Accessible Accessible_child2 =
 {
     { &AccessibleVtbl },
+    { &Accessible2Vtbl },
     { &OleWindowVtbl },
+    { &ServiceProviderVtbl },
     1,
     &Accessible.IAccessible_iface,
     0, 0,
     0, 0, 0, NULL,
     0, 0, 0, 0,
+    FALSE, 0,
 };
 
 static IAccessible *acc_client;
@@ -1002,6 +1444,166 @@ static void set_accessible_props(struct Accessible *acc, INT role, INT state,
     acc->height = height;
 }
 
+static void set_accessible_ia2_props(struct Accessible *acc, BOOL enable_ia2, LONG unique_id)
+{
+    acc->enable_ia2 = enable_ia2;
+    acc->unique_id = unique_id;
+}
+
+static void test_uia_prov_from_acc_ia2(void)
+{
+    IRawElementProviderSimple *elprov, *elprov2;
+    HRESULT hr;
+
+    /* Only one exposes an IA2 interface, no match. */
+    set_accessible_props(&Accessible, 0, 0, 0, L"acc_name", 0, 0, 0, 0);
+    set_accessible_ia2_props(&Accessible, TRUE, 0);
+    set_accessible_props(&Accessible2, 0, 0, 0, L"acc_name", 0, 0, 0, 0);
+    set_accessible_ia2_props(&Accessible2, FALSE, 0);
+
+    hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    if (Accessible.ref != 3)
+    {
+        IRawElementProviderSimple_Release(elprov);
+        win_skip("UiaProviderFromIAccessible has no IAccessible2 support, skipping tests.\n");
+        return;
+    }
+
+    acc_client = &Accessible2.IAccessible_iface;
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL, elprov %p\n", elprov2);
+    ok(Accessible2.ref == 1, "Unexpected refcnt %ld\n", Accessible2.ref);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+
+    elprov2 = (void *)0xdeadbeef;
+    acc_client = NULL;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL, elprov %p\n", elprov2);
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /*
+     * If &Accessible returns a failure code on get_uniqueID, &Accessible2's
+     * uniqueID is not checked.
+     */
+    set_accessible_ia2_props(&Accessible, TRUE, 0);
+    set_accessible_ia2_props(&Accessible2, TRUE, 0);
+    hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    acc_client = &Accessible2.IAccessible_iface;
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    SET_EXPECT(Accessible_get_accRole);
+    SET_EXPECT(Accessible_get_accState);
+    SET_EXPECT(Accessible_get_accChildCount);
+    SET_EXPECT(Accessible_accLocation);
+    SET_EXPECT(Accessible_get_accName);
+    SET_EXPECT(Accessible_get_uniqueID);
+    SET_EXPECT(Accessible2_get_accChildCount);
+    SET_EXPECT(Accessible2_get_accName);
+    SET_EXPECT(Accessible2_QI_IAccIdentity);
+    SET_EXPECT(Accessible2_get_accParent);
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elprov2, "elprov == NULL, elprov %p\n", elprov2);
+    ok(Accessible2.ref == 1, "Unexpected refcnt %ld\n", Accessible2.ref);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+    CHECK_CALLED(Accessible_get_accRole);
+    CHECK_CALLED(Accessible_get_accState);
+    CHECK_CALLED(Accessible_get_accChildCount);
+    CHECK_CALLED(Accessible_accLocation);
+    CHECK_CALLED(Accessible_get_accName);
+    CHECK_CALLED(Accessible_get_uniqueID);
+    CHECK_CALLED(Accessible2_get_accChildCount);
+    CHECK_CALLED(Accessible2_get_accName);
+    todo_wine CHECK_CALLED(Accessible2_QI_IAccIdentity);
+    todo_wine CHECK_CALLED(Accessible2_get_accParent);
+    IRawElementProviderSimple_Release(elprov2);
+
+    elprov2 = (void *)0xdeadbeef;
+    acc_client = NULL;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elprov2, "elprov == NULL, elprov %p\n", elprov2);
+    IRawElementProviderSimple_Release(elprov2);
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /* Unique ID matches. */
+    set_accessible_ia2_props(&Accessible, TRUE, 1);
+    set_accessible_ia2_props(&Accessible2, TRUE, 1);
+    hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    acc_client = &Accessible2.IAccessible_iface;
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    SET_EXPECT(Accessible_get_uniqueID);
+    SET_EXPECT(Accessible2_get_uniqueID);
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elprov2, "elprov == NULL, elprov %p\n", elprov2);
+    ok(Accessible2.ref == 1, "Unexpected refcnt %ld\n", Accessible2.ref);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+    CHECK_CALLED(Accessible_get_uniqueID);
+    CHECK_CALLED(Accessible2_get_uniqueID);
+    IRawElementProviderSimple_Release(elprov2);
+
+    elprov2 = (void *)0xdeadbeef;
+    acc_client = NULL;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elprov2, "elprov == NULL, elprov %p\n", elprov2);
+    IRawElementProviderSimple_Release(elprov2);
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /* Unique ID mismatch. */
+    set_accessible_ia2_props(&Accessible, TRUE, 1);
+    set_accessible_ia2_props(&Accessible2, TRUE, 2);
+    hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    acc_client = &Accessible2.IAccessible_iface;
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    SET_EXPECT(Accessible_get_uniqueID);
+    SET_EXPECT(Accessible2_get_uniqueID);
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL, elprov %p\n", elprov2);
+    ok(Accessible2.ref == 1, "Unexpected refcnt %ld\n", Accessible2.ref);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+    CHECK_CALLED(Accessible_get_uniqueID);
+    CHECK_CALLED(Accessible2_get_uniqueID);
+
+    elprov2 = (void *)0xdeadbeef;
+    acc_client = NULL;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL, elprov %p\n", elprov2);
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    set_accessible_props(&Accessible, 0, 0, 0, NULL, 0, 0, 0, 0);
+    set_accessible_ia2_props(&Accessible, FALSE, 0);
+    set_accessible_props(&Accessible2, 0, 0, 0, NULL, 0, 0, 0, 0);
+    set_accessible_ia2_props(&Accessible2, FALSE, 0);
+}
+
 #define check_fragment_acc( fragment, acc, cid) \
         check_fragment_acc_( (fragment), (acc), (cid), __LINE__)
 static void check_fragment_acc_(IRawElementProviderFragment *elfrag, IAccessible *acc,
@@ -2091,6 +2693,7 @@ static void test_UiaProviderFromIAccessible(void)
 
     test_uia_prov_from_acc_properties();
     test_uia_prov_from_acc_navigation();
+    test_uia_prov_from_acc_ia2();
 
     CoUninitialize();
     DestroyWindow(hwnd);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 11c8e17b766..5766425f33b 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -24,6 +24,7 @@
 #include "wine/debug.h"
 #include "wine/heap.h"
 #include "initguid.h"
+#include "wine/iaccessible2.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
 
@@ -54,6 +55,28 @@ static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, ULONG flag)
     return FALSE;
 }
 
+static IAccessible2 *msaa_acc_get_ia2(IAccessible *acc)
+{
+    IServiceProvider *serv_prov;
+    IAccessible2 *ia2 = NULL;
+    HRESULT hr;
+
+    hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
+    if (SUCCEEDED(hr))
+    {
+        hr = IServiceProvider_QueryService(serv_prov, &IID_IAccessible2, &IID_IAccessible2, (void **)&ia2);
+        IServiceProvider_Release(serv_prov);
+        if (SUCCEEDED(hr) && ia2)
+            return ia2;
+    }
+
+    hr = IAccessible_QueryInterface(acc, &IID_IAccessible2, (void **)&ia2);
+    if (SUCCEEDED(hr) && ia2)
+        return ia2;
+
+    return NULL;
+}
+
 static IAccessible *msaa_acc_da_unwrap(IAccessible *acc)
 {
     IServiceProvider *sp;
@@ -149,8 +172,10 @@ static HRESULT msaa_acc_prop_match(IAccessible *acc, IAccessible *acc2)
 
 static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2)
 {
+    IAccessible2 *ia2[2] = { NULL, NULL };
     IUnknown *unk, *unk2;
     BOOL matched = FALSE;
+    LONG unique_id[2];
     BSTR name[2];
     VARIANT cid;
     HRESULT hr;
@@ -165,6 +190,26 @@ static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2)
         goto exit;
     }
 
+    ia2[0] = msaa_acc_get_ia2(acc);
+    ia2[1] = msaa_acc_get_ia2(acc2);
+    if ((ia2[0] || ia2[1]) && !(ia2[0] && ia2[1]))
+        goto exit;
+    if (ia2[0])
+    {
+        hr = IAccessible2_get_uniqueID(ia2[0], &unique_id[0]);
+        if (SUCCEEDED(hr))
+        {
+            hr = IAccessible2_get_uniqueID(ia2[1], &unique_id[1]);
+            if (SUCCEEDED(hr))
+            {
+                if (unique_id[0] == unique_id[1])
+                    matched = TRUE;
+
+                goto exit;
+            }
+        }
+    }
+
     hr = msaa_acc_prop_match(acc, acc2);
     if (FAILED(hr))
         goto exit;
@@ -201,6 +246,10 @@ exit:
     IUnknown_Release(unk2);
     IAccessible_Release(acc);
     IAccessible_Release(acc2);
+    if (ia2[0])
+        IAccessible2_Release(ia2[0]);
+    if (ia2[1])
+        IAccessible2_Release(ia2[1]);
 
     return matched;
 }
@@ -456,6 +505,7 @@ struct msaa_provider {
     LONG refcount;
 
     IAccessible *acc;
+    IAccessible2 *ia2;
     VARIANT cid;
     HWND hwnd;
     LONG control_type;
@@ -535,6 +585,8 @@ ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
         IAccessible_Release(msaa_prov->acc);
         if (msaa_prov->parent)
             IAccessible_Release(msaa_prov->parent);
+        if (msaa_prov->ia2)
+            IAccessible2_Release(msaa_prov->ia2);
         heap_free(msaa_prov);
     }
 
@@ -1069,6 +1121,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD
     variant_init_i4(&msaa_prov->cid, child_id);
     msaa_prov->acc = acc;
     IAccessible_AddRef(acc);
+    msaa_prov->ia2 = msaa_acc_get_ia2(acc);
     *elprov = &msaa_prov->IRawElementProviderSimple_iface;
 
     return S_OK;
-- 
GitLab

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



More information about the wine-devel mailing list