[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