[PATCH v3 1/3] uiautomationcore: Implement get_HostRawElementProvider for MSAA providers.

Connor McAdams wine at gitlab.winehq.org
Fri Jun 10 08:42:24 CDT 2022


From: Connor McAdams <cmcadams at codeweavers.com>

Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
 dlls/uiautomationcore/tests/uiautomation.c | 479 ++++++++++++++++++++-
 dlls/uiautomationcore/uia_provider.c       | 190 +++++++-
 2 files changed, 654 insertions(+), 15 deletions(-)

diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index f3e4b89a120..6ed3c907a40 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -58,13 +58,21 @@ 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_accChildCount);
+DEFINE_EXPECT(Accessible_get_accName);
 DEFINE_EXPECT(Accessible_get_accRole);
 DEFINE_EXPECT(Accessible_get_accState);
+DEFINE_EXPECT(Accessible_accLocation);
+DEFINE_EXPECT(Accessible2_get_accParent);
+DEFINE_EXPECT(Accessible2_get_accChildCount);
+DEFINE_EXPECT(Accessible2_get_accName);
+DEFINE_EXPECT(Accessible2_get_accRole);
+DEFINE_EXPECT(Accessible2_get_accState);
+DEFINE_EXPECT(Accessible2_accLocation);
+DEFINE_EXPECT(Accessible2_QI_IAccIdentity);
 DEFINE_EXPECT(Accessible_child_accNavigate);
 DEFINE_EXPECT(Accessible_child_get_accParent);
 
-static IAccessible *acc_client;
-
 static BOOL check_variant_i4(VARIANT *v, int val)
 {
     if (V_VT(v) == VT_I4 && V_I4(v) == val)
@@ -92,7 +100,10 @@ static struct Accessible
     HWND ow_hwnd;
     INT role;
     INT state;
-} Accessible, Accessible_child;
+    LONG child_count;
+    LPCWSTR name;
+    LONG left, top, width, height;
+} Accessible, Accessible2, Accessible_child;
 
 static inline struct Accessible* impl_from_Accessible(IAccessible *iface)
 {
@@ -104,6 +115,14 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid,
     struct Accessible *This = impl_from_Accessible(iface);
 
     *obj = NULL;
+    if (IsEqualIID(riid, &IID_IAccIdentity))
+    {
+        if (This == &Accessible2)
+            CHECK_EXPECT(Accessible2_QI_IAccIdentity);
+        ok(This == &Accessible2, "unexpected call\n");
+        return E_NOINTERFACE;
+    }
+
     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch) ||
             IsEqualIID(riid, &IID_IAccessible))
         *obj = iface;
@@ -162,6 +181,8 @@ static HRESULT WINAPI Accessible_get_accParent(IAccessible *iface, IDispatch **o
 
     if (This == &Accessible_child)
         CHECK_EXPECT(Accessible_child_get_accParent);
+    else if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_accParent);
     else
         CHECK_EXPECT(Accessible_get_accParent);
 
@@ -174,7 +195,19 @@ static HRESULT WINAPI Accessible_get_accParent(IAccessible *iface, IDispatch **o
 
 static HRESULT WINAPI Accessible_get_accChildCount(IAccessible *iface, LONG *out_count)
 {
-    ok(0, "unexpected call\n");
+    struct Accessible *This = impl_from_Accessible(iface);
+
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_accChildCount);
+    else
+        CHECK_EXPECT(Accessible_get_accChildCount);
+
+    if (This->child_count)
+    {
+        *out_count = This->child_count;
+        return S_OK;
+    }
+
     return E_NOTIMPL;
 }
 
@@ -188,7 +221,20 @@ static HRESULT WINAPI Accessible_get_accChild(IAccessible *iface, VARIANT child_
 static HRESULT WINAPI Accessible_get_accName(IAccessible *iface, VARIANT child_id,
         BSTR *out_name)
 {
-    ok(0, "unexpected call\n");
+    struct Accessible *This = impl_from_Accessible(iface);
+
+    *out_name = NULL;
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_accName);
+    else
+        CHECK_EXPECT(Accessible_get_accName);
+
+    if (This->name)
+    {
+        *out_name = SysAllocString(This->name);
+        return S_OK;
+    }
+
     return E_NOTIMPL;
 }
 
@@ -211,8 +257,10 @@ static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_i
 {
     struct Accessible *This = impl_from_Accessible(iface);
 
-    ok(This == &Accessible, "unexpected call\n");
-    CHECK_EXPECT(Accessible_get_accRole);
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_accRole);
+    else
+        CHECK_EXPECT(Accessible_get_accRole);
 
     if (This->role)
     {
@@ -229,8 +277,10 @@ static HRESULT WINAPI Accessible_get_accState(IAccessible *iface, VARIANT child_
 {
     struct Accessible *This = impl_from_Accessible(iface);
 
-    ok(This == &Accessible, "unexpected call\n");
-    CHECK_EXPECT(Accessible_get_accState);
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_get_accState);
+    else
+        CHECK_EXPECT(Accessible_get_accState);
 
     if (This->state)
     {
@@ -292,7 +342,22 @@ static HRESULT WINAPI Accessible_accSelect(IAccessible *iface, LONG select_flags
 static HRESULT WINAPI Accessible_accLocation(IAccessible *iface, LONG *out_left,
         LONG *out_top, LONG *out_width, LONG *out_height, VARIANT child_id)
 {
-    ok(0, "unexpected call\n");
+    struct Accessible *This = impl_from_Accessible(iface);
+
+    if (This == &Accessible2)
+        CHECK_EXPECT(Accessible2_accLocation);
+    else
+        CHECK_EXPECT(Accessible_accLocation);
+
+    if (This->width && This->height)
+    {
+        *out_left = This->left;
+        *out_top = This->top;
+        *out_width = This->width;
+        *out_height = This->height;
+        return S_OK;
+    }
+
     return E_NOTIMPL;
 }
 
@@ -430,8 +495,21 @@ static struct Accessible Accessible =
     1,
     NULL,
     0, 0,
+    0, 0, 0, NULL,
+    0, 0, 0, 0,
+};
+
+static struct Accessible Accessible2 =
+{
+    { &AccessibleVtbl },
+    { &OleWindowVtbl },
+    1,
+    NULL,
     0, 0,
+    0, 0, 0, NULL,
+    0, 0, 0, 0,
 };
+
 static struct Accessible Accessible_child =
 {
     { &AccessibleVtbl },
@@ -439,9 +517,11 @@ static struct Accessible Accessible_child =
     1,
     &Accessible.IAccessible_iface,
     0, 0,
-    0, 0,
+    0, 0, 0, NULL,
+    0, 0, 0, 0,
 };
 
+static IAccessible *acc_client;
 static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     switch (message)
@@ -782,6 +862,20 @@ static const struct msaa_state_uia_prop msaa_state_uia_props[] = {
     { STATE_SYSTEM_PROTECTED,    UIA_IsPasswordPropertyId },
 };
 
+static void set_accessible_props(struct Accessible *acc, INT role, INT state,
+        LONG child_count, LPCWSTR name, LONG left, LONG top, LONG width, LONG height)
+{
+
+    acc->role = role;
+    acc->state = state;
+    acc->child_count = child_count;
+    acc->name = name;
+    acc->left = left;
+    acc->top = top;
+    acc->width = width;
+    acc->height = height;
+}
+
 static void test_uia_prov_from_acc_properties(void)
 {
     IRawElementProviderSimple *elprov;
@@ -883,7 +977,7 @@ static void test_uia_prov_from_acc_properties(void)
 
 static void test_UiaProviderFromIAccessible(void)
 {
-    IRawElementProviderSimple *elprov;
+    IRawElementProviderSimple *elprov, *elprov2;
     enum ProviderOptions prov_opt;
     IAccessible *acc;
     WNDCLASSA cls;
@@ -891,7 +985,7 @@ static void test_UiaProviderFromIAccessible(void)
     HWND hwnd;
     VARIANT v;
 
-
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
     cls.style = 0;
     cls.lpfnWndProc = test_wnd_proc;
     cls.cbClsExtra = 0;
@@ -997,11 +1091,370 @@ static void test_UiaProviderFromIAccessible(void)
     hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, 1, UIA_PFIA_DEFAULT, &elprov);
     ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
     ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /*
+     * Simple child element (IAccessible without CHILDID_SELF) cannot be root
+     * IAccessible. No checks against the root HWND IAccessible will be done.
+     */
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL\n");
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /*
+     * &Accessible.IAccessible_iface will be compared against the default
+     * client accessible object. Since we have all properties set to 0,
+     * we return failure HRESULTs and all properties will get queried but not
+     * compared.
+     */
+    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);
+
+    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);
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL\n");
+    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);
+
+    /* Second call won't send WM_GETOBJECT. */
+    elprov2 = (void *)0xdeadbeef;
+    hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!elprov2, "elprov != NULL\n");
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /*
+     * Return &Accessible.IAccessible_iface in response to OBJID_CLIENT,
+     * interface pointers will be compared, no method calls to check property
+     * values.
+     */
+    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);
+
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    elprov2 = (void *)0xdeadbeef;
+    acc_client = &Accessible.IAccessible_iface;
+    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);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+
+    /* Second call, no checks. */
+    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);
+
+    /*
+     * Return &Accessible2.IAccessible_iface in response to OBJID_CLIENT,
+     * interface pointers won't match, so properties will be compared.
+     */
+    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);
+
+    set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1,
+            L"acc_name", 0, 0, 50, 50);
+    set_accessible_props(&Accessible2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1,
+            L"acc_name", 0, 0, 50, 50);
+
+    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(Accessible2_get_accRole);
+    SET_EXPECT(Accessible2_get_accState);
+    SET_EXPECT(Accessible2_get_accChildCount);
+    SET_EXPECT(Accessible2_accLocation);
+    SET_EXPECT(Accessible2_get_accName);
+    /*
+     * The IAccessible returned by WM_GETOBJECT will be checked for an
+     * IAccIdentity interface to see if Dynamic Annotation properties should
+     * be queried. If not present on the current IAccessible, it will check
+     * the parent IAccessible for one.
+     */
+    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(Accessible2_get_accRole);
+    CHECK_CALLED(Accessible2_get_accState);
+    CHECK_CALLED(Accessible2_get_accChildCount);
+    CHECK_CALLED(Accessible2_accLocation);
+    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);
+
+    /*
+     * If a failure HRESULT is returned from the IRawElementProviderSimple
+     * IAccessible, the corresponding AOFW IAccessible method isn't called.
+     * An exception is get_accChildCount, which is always called, but only
+     * checked if the HRESULT return value is not a failure. If Role/State/Name
+     * are not queried, no IAccIdentity check is done.
+     */
+    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);
+
+    set_accessible_props(&Accessible, 0, 0, 0, NULL, 0, 0, 0, 0);
+    set_accessible_props(&Accessible2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1,
+            L"acc_name", 0, 0, 50, 50);
+
+    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(Accessible2_get_accChildCount);
+    SET_EXPECT(Accessible_accLocation);
+    SET_EXPECT(Accessible_get_accName);
+    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);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+    CHECK_CALLED(Accessible_get_accRole);
+    CHECK_CALLED(Accessible_get_accState);
+    CHECK_CALLED(Accessible_get_accChildCount);
+    CHECK_CALLED(Accessible2_get_accChildCount);
+    CHECK_CALLED(Accessible_accLocation);
+    CHECK_CALLED(Accessible_get_accName);
+
+    acc_client = NULL;
+    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);
+
+    IRawElementProviderSimple_Release(elprov);
+    ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+
+    /*
+     * Properties are checked in a sequence of accRole, accState,
+     * accChildCount, accLocation, and finally accName. If a mismatch is found
+     * early in the sequence, the rest aren't checked.
+     */
+    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);
+
+    set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 0, NULL, 0, 0, 0, 0);
+    set_accessible_props(&Accessible2, ROLE_SYSTEM_CLIENT, STATE_SYSTEM_FOCUSABLE, 0, NULL, 0, 0, 0, 0);
+
+    acc_client = &Accessible2.IAccessible_iface;
+    SET_EXPECT(winproc_GETOBJECT_CLIENT);
+    SET_EXPECT(Accessible_get_accRole);
+    SET_EXPECT(Accessible2_get_accRole);
+    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);
+    CHECK_CALLED(winproc_GETOBJECT_CLIENT);
+    CHECK_CALLED(Accessible_get_accRole);
+    CHECK_CALLED(Accessible2_get_accRole);
+    todo_wine CHECK_CALLED(Accessible2_QI_IAccIdentity);
+    todo_wine CHECK_CALLED(Accessible2_get_accParent);
+
+    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);
+
+    /* 4/5 properties match, considered a match. */
+    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);
+
+    set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1, NULL, 0, 0, 50, 50);
+    set_accessible_props(&Accessible2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1, NULL, 0, 0, 50, 50);
+
+    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(Accessible2_get_accRole);
+    SET_EXPECT(Accessible2_get_accState);
+    SET_EXPECT(Accessible2_get_accChildCount);
+    SET_EXPECT(Accessible2_accLocation);
+    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(Accessible2_get_accRole);
+    CHECK_CALLED(Accessible2_get_accState);
+    CHECK_CALLED(Accessible2_get_accChildCount);
+    CHECK_CALLED(Accessible2_accLocation);
+    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);
+
+    /* 3/5 properties match, not considered a match. */
+    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);
+
+    set_accessible_props(&Accessible, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1, NULL, 0, 0, 0, 0);
+    set_accessible_props(&Accessible2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1, NULL, 0, 0, 0, 0);
+
+    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(Accessible2_get_accRole);
+    SET_EXPECT(Accessible2_get_accState);
+    SET_EXPECT(Accessible2_get_accChildCount);
+    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);
+    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(Accessible2_get_accRole);
+    CHECK_CALLED(Accessible2_get_accState);
+    CHECK_CALLED(Accessible2_get_accChildCount);
+    todo_wine CHECK_CALLED(Accessible2_QI_IAccIdentity);
+    todo_wine CHECK_CALLED(Accessible2_get_accParent);
+
+    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);
+
+    /* Only name matches, considered a match. */
+    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);
+
+    set_accessible_props(&Accessible, 0, 0, 0, L"acc_name", 0, 0, 0, 0);
+    set_accessible_props(&Accessible2, 0, 0, 0, L"acc_name", 0, 0, 0, 0);
+
+    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(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(Accessible2_get_accChildCount);
+    CHECK_CALLED(Accessible2_get_accName);
+    todo_wine CHECK_CALLED(Accessible2_QI_IAccIdentity);
+    todo_wine CHECK_CALLED(Accessible2_get_accParent);
+
+    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);
 
     test_uia_prov_from_acc_properties();
 
+    CoUninitialize();
     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 dae0187596d..6c3bd1c2186 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -23,9 +23,12 @@
 
 #include "wine/debug.h"
 #include "wine/heap.h"
+#include "initguid.h"
 
 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;
@@ -51,6 +54,157 @@ static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, ULONG flag)
     return FALSE;
 }
 
+static IAccessible *msaa_acc_da_unwrap(IAccessible *acc)
+{
+    IServiceProvider *sp;
+    IAccessible *acc2;
+    HRESULT hr;
+
+    hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void**)&sp);
+    if (SUCCEEDED(hr))
+    {
+        hr = IServiceProvider_QueryService(sp, &SID_AccFromDAWrapper, &IID_IAccessible, (void**)&acc2);
+        IServiceProvider_Release(sp);
+    }
+
+    if (SUCCEEDED(hr) && acc2)
+        return acc2;
+
+    IAccessible_AddRef(acc);
+    return acc;
+}
+
+/*
+ * Compare role, state, child count, and location properties of the two
+ * IAccessibles. If all four are successfully retrieved and are equal, this is
+ * considered a match.
+ */
+static HRESULT msaa_acc_prop_match(IAccessible *acc, IAccessible *acc2)
+{
+    BOOL role_match, state_match, child_count_match, location_match;
+    LONG child_count[2], left[2], top[2], width[2], height[2];
+    VARIANT cid, v, v2;
+    HRESULT hr, hr2;
+
+    role_match = state_match = child_count_match = location_match = FALSE;
+    variant_init_i4(&cid, CHILDID_SELF);
+    hr = IAccessible_get_accRole(acc, cid, &v);
+    if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
+    {
+        VariantInit(&v2);
+        hr = IAccessible_get_accRole(acc2, cid, &v2);
+        if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
+        {
+            if (V_I4(&v) != V_I4(&v2))
+                return E_FAIL;
+
+            role_match = TRUE;
+        }
+    }
+
+    VariantInit(&v);
+    hr = IAccessible_get_accState(acc, cid, &v);
+    if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
+    {
+        VariantInit(&v2);
+        hr = IAccessible_get_accState(acc2, cid, &v2);
+        if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
+        {
+            if (V_I4(&v) != V_I4(&v2))
+                return E_FAIL;
+
+            state_match = TRUE;
+        }
+    }
+
+    hr = IAccessible_get_accChildCount(acc, &child_count[0]);
+    hr2 = IAccessible_get_accChildCount(acc2, &child_count[1]);
+    if (SUCCEEDED(hr) && SUCCEEDED(hr2))
+    {
+        if (child_count[0] != child_count[1])
+            return E_FAIL;
+
+        child_count_match = TRUE;
+    }
+
+    hr = IAccessible_accLocation(acc, &left[0], &top[0], &width[0], &height[0], cid);
+    if (SUCCEEDED(hr))
+    {
+        hr = IAccessible_accLocation(acc2, &left[1], &top[1], &width[1], &height[1], cid);
+        if (SUCCEEDED(hr))
+        {
+            if ((left[0] != left[1]) || (top[0] != top[1]) || (width[0] != width[1]) ||
+                    (height[0] != height[1]))
+                return E_FAIL;
+
+            location_match = TRUE;
+        }
+    }
+
+    if (role_match && state_match && child_count_match && location_match)
+        return S_OK;
+
+    return S_FALSE;
+}
+
+static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2)
+{
+    IUnknown *unk, *unk2;
+    BOOL matched = FALSE;
+    BSTR name[2];
+    VARIANT cid;
+    HRESULT hr;
+
+    acc = msaa_acc_da_unwrap(acc);
+    acc2 = msaa_acc_da_unwrap(acc2);
+    IAccessible_QueryInterface(acc, &IID_IUnknown, (void**)&unk);
+    IAccessible_QueryInterface(acc2, &IID_IUnknown, (void**)&unk2);
+    if (unk == unk2)
+    {
+        matched = TRUE;
+        goto exit;
+    }
+
+    hr = msaa_acc_prop_match(acc, acc2);
+    if (FAILED(hr))
+        goto exit;
+    if (hr == S_OK)
+        matched = TRUE;
+
+    variant_init_i4(&cid, CHILDID_SELF);
+    hr = IAccessible_get_accName(acc, cid, &name[0]);
+    if (SUCCEEDED(hr))
+    {
+        hr = IAccessible_get_accName(acc2, cid, &name[1]);
+        if (SUCCEEDED(hr))
+        {
+            if (!name[0] && !name[1])
+                matched = TRUE;
+            else if (!name[0] || !name[1])
+                matched = FALSE;
+            else
+            {
+                if (!wcscmp(name[0], name[1]))
+                    matched = TRUE;
+                else
+                    matched = FALSE;
+            }
+
+            SysFreeString(name[1]);
+        }
+
+        SysFreeString(name[0]);
+    }
+
+exit:
+    IUnknown_Release(unk);
+    IUnknown_Release(unk2);
+    IAccessible_Release(acc);
+    IAccessible_Release(acc2);
+
+    return matched;
+}
+
 static LONG msaa_role_to_uia_control_type(LONG role)
 {
     switch (role)
@@ -141,8 +295,34 @@ struct msaa_provider {
     VARIANT cid;
     HWND hwnd;
     LONG control_type;
+
+    BOOL root_acc_check_ran;
+    BOOL is_root_acc;
 };
 
+static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
+{
+    IAccessible *acc;
+    HRESULT hr;
+
+    if (msaa_prov->root_acc_check_ran)
+        return msaa_prov->is_root_acc;
+
+    msaa_prov->root_acc_check_ran = TRUE;
+    if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+        return FALSE;
+
+    hr = AccessibleObjectFromWindow(msaa_prov->hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
+    if (FAILED(hr))
+        return FALSE;
+
+    if (msaa_acc_compare(msaa_prov->acc, acc))
+        msaa_prov->is_root_acc = TRUE;
+
+    IAccessible_Release(acc);
+    return msaa_prov->is_root_acc;
+}
+
 static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface)
 {
     return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderSimple_iface);
@@ -264,9 +444,15 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
 HRESULT WINAPI msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface,
         IRawElementProviderSimple **ret_val)
 {
-    FIXME("%p, %p: stub!\n", iface, ret_val);
+    struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
+
+    TRACE("%p, %p\n", iface, ret_val);
+
     *ret_val = NULL;
-    return E_NOTIMPL;
+    if (msaa_check_root_acc(msaa_prov))
+        return UiaHostProviderFromHwnd(msaa_prov->hwnd, ret_val);
+
+    return S_OK;
 }
 
 static const IRawElementProviderSimpleVtbl msaa_provider_vtbl = {
-- 
GitLab


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



More information about the wine-devel mailing list