From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 479 ++++++++++++++++++++- dlls/uiautomationcore/uia_provider.c | 173 +++++++- 2 files changed, 637 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..700eaae4a2c 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,140 @@ 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; +} + +static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2) +{ + LONG child_count[2], left[2], top[2], width[2], height[2]; + BSTR name[2] = { NULL, NULL }; + BOOL matched = FALSE; + int match_count = 0; + VARIANT cid, v, v2; + HRESULT hr, hr2; + + acc = msaa_acc_da_unwrap(acc); + acc2 = msaa_acc_da_unwrap(acc2); + if (acc == acc2) + { + matched = TRUE; + goto exit; + } + else + { + IUnknown *unk, *unk2; + + IAccessible_QueryInterface(acc, &IID_IUnknown, (void**)&unk); + IUnknown_Release(unk); + IAccessible_QueryInterface(acc2, &IID_IUnknown, (void**)&unk2); + IUnknown_Release(unk2); + if (unk == unk2) + { + matched = TRUE; + goto exit; + } + } + + variant_init_i4(&cid, CHILDID_SELF); + VariantInit(&v); + 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)) + goto exit; + + match_count++; + } + } + + 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)) + goto exit; + + match_count++; + } + } + + 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]) + goto exit; + + match_count++; + } + + 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])) + goto exit; + + match_count++; + } + } + + 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]) && (!name[0] || !name[1])) || + ((name[0] && name[1]) && wcscmp(name[0], name[1]))) + goto exit; + + /* accName match is always a confirmed match. */ + matched = TRUE; + } + } + + if (match_count == 4) + matched = TRUE; + +exit: + + SysFreeString(name[0]); + SysFreeString(name[1]); + IAccessible_Release(acc); + IAccessible_Release(acc2); + + return matched; +} + static LONG msaa_role_to_uia_control_type(LONG role) { switch (role) @@ -141,8 +278,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 +427,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 = {
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- include/uiautomationcore.idl | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/include/uiautomationcore.idl b/include/uiautomationcore.idl index 170b0c76de1..c4aa258b5af 100644 --- a/include/uiautomationcore.idl +++ b/include/uiautomationcore.idl @@ -151,4 +151,35 @@ library UIA
HRESULT GetFocus([out, retval] IRawElementProviderFragment **pRetVal); } + + [ + object, + uuid(e44c3566-915d-4070-99c6-047bff5a08f5), + pointer_default(unique), + oleautomation + ] + interface ILegacyIAccessibleProvider : IUnknown + { + HRESULT Select(long flagsSelect); + HRESULT DoDefaultAction(); + HRESULT SetValue(LPCWSTR szValue); + HRESULT GetIAccessible([out, retval] IAccessible **ppAccessible); + + [propget] HRESULT ChildId([out, retval] int *pRetVal); + [propget] HRESULT Name([out, retval] BSTR *pszName); + [propget] HRESULT Value([out, retval] BSTR *pszValue); + [propget] HRESULT Description([out, retval] BSTR *pszDescription); + [propget] HRESULT Role([out, retval] DWORD *pdwRole); + [propget] HRESULT State([out, retval] DWORD *pdwState); + [propget] HRESULT Help([out, retval] BSTR *pszHelp); + [propget] HRESULT KeyboardShortcut([out, retval] BSTR *pszKeyboardShortcut); + /* + * FIXME: Current versions of Windows SDK use + * SAFEARRAY(IRawElementProviderSimple *) instead of + * SAFEARRAY(VARIANT). The new type is currently unsupported + * in widl, we should switch to it when it is. + */ + HRESULT GetSelection([out, retval] SAFEARRAY(VARIANT) *pvarSelectedChildren); + [propget] HRESULT DefaultAction([out, retval] BSTR *pszDefaultAction); + } }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 57 +++++++ dlls/uiautomationcore/uia_provider.c | 167 ++++++++++++++++++++- 2 files changed, 222 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6ed3c907a40..6a7e9afbdc0 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -89,6 +89,20 @@ static BOOL check_variant_bool(VARIANT *v, BOOL val) return FALSE; }
+static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) +{ + IUnknown *unk1, *unk2; + + if(iface1 == iface2) + return TRUE; + + IUnknown_QueryInterface(iface1, &IID_IUnknown, (void**)&unk1); + IUnknown_Release(unk1); + IUnknown_QueryInterface(iface2, &IID_IUnknown, (void**)&unk2); + IUnknown_Release(unk2); + return unk1 == unk2; +} + static struct Accessible { IAccessible IAccessible_iface; @@ -977,13 +991,16 @@ static void test_uia_prov_from_acc_properties(void)
static void test_UiaProviderFromIAccessible(void) { + ILegacyIAccessibleProvider *accprov; IRawElementProviderSimple *elprov, *elprov2; enum ProviderOptions prov_opt; IAccessible *acc; + IUnknown *unk; WNDCLASSA cls; HRESULT hr; HWND hwnd; VARIANT v; + INT cid;
CoInitializeEx(NULL, COINIT_MULTITHREADED); cls.style = 0; @@ -1084,6 +1101,26 @@ static void test_UiaProviderFromIAccessible(void) ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v)); VariantClear(&v);
+ hr = IRawElementProviderSimple_GetPatternProvider(elprov, UIA_LegacyIAccessiblePatternId, &unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!unk, "unk == NULL\n"); + ok(iface_cmp((IUnknown *)elprov, unk), "unk != elprov\n"); + + hr = IUnknown_QueryInterface(unk, &IID_ILegacyIAccessibleProvider, (void **)&accprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!accprov, "accprov == NULL\n"); + + hr = ILegacyIAccessibleProvider_get_ChildId(accprov, &cid); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(cid == CHILDID_SELF, "cid != CHILDID_SELF\n"); + + hr = ILegacyIAccessibleProvider_GetIAccessible(accprov, &acc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(acc == &Accessible.IAccessible_iface, "acc != &Accessible.IAccessible_iface\n"); + IAccessible_Release(acc); + IUnknown_Release(unk); + ILegacyIAccessibleProvider_Release(accprov); + IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
@@ -1101,6 +1138,26 @@ static void test_UiaProviderFromIAccessible(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!elprov2, "elprov != NULL\n");
+ hr = IRawElementProviderSimple_GetPatternProvider(elprov, UIA_LegacyIAccessiblePatternId, &unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!unk, "unk == NULL\n"); + ok(iface_cmp((IUnknown *)elprov, unk), "unk != elprov\n"); + + hr = IUnknown_QueryInterface(unk, &IID_ILegacyIAccessibleProvider, (void **)&accprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!accprov, "accprov == NULL\n"); + + hr = ILegacyIAccessibleProvider_get_ChildId(accprov, &cid); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(cid == 1, "cid != CHILDID_SELF\n"); + + hr = ILegacyIAccessibleProvider_GetIAccessible(accprov, &acc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(acc == &Accessible.IAccessible_iface, "acc != &Accessible.IAccessible_iface\n"); + IAccessible_Release(acc); + IUnknown_Release(unk); + ILegacyIAccessibleProvider_Release(accprov); + IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 700eaae4a2c..f6a1a7d7bf3 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -272,6 +272,7 @@ static LONG msaa_role_to_uia_control_type(LONG role) */ struct msaa_provider { IRawElementProviderSimple IRawElementProviderSimple_iface; + ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface; LONG refcount;
IAccessible *acc; @@ -313,9 +314,13 @@ static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderS
HRESULT WINAPI msaa_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv) { + struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface); + *ppv = NULL; if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown)) *ppv = iface; + else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider)) + *ppv = &msaa_prov->ILegacyIAccessibleProvider_iface; else return E_NOINTERFACE;
@@ -360,9 +365,20 @@ HRESULT WINAPI msaa_provider_get_ProviderOptions(IRawElementProviderSimple *ifac HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface, PATTERNID pattern_id, IUnknown **ret_val) { - FIXME("%p, %d, %p: stub!\n", iface, pattern_id, ret_val); + TRACE("%p, %d, %p\n", iface, pattern_id, ret_val); + *ret_val = NULL; - return E_NOTIMPL; + switch (pattern_id) + { + case UIA_LegacyIAccessiblePatternId: + return IRawElementProviderSimple_QueryInterface(iface, &IID_IUnknown, (void **)ret_val); + + default: + FIXME("Unimplemented patternId %d\n", pattern_id); + break; + } + + return S_OK; }
HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, @@ -448,6 +464,152 @@ static const IRawElementProviderSimpleVtbl msaa_provider_vtbl = { msaa_provider_get_HostRawElementProvider, };
+/* + * ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible + * providers. + */ +static inline struct msaa_provider *impl_from_msaa_acc_provider(ILegacyIAccessibleProvider *iface) +{ + return CONTAINING_RECORD(iface, struct msaa_provider, ILegacyIAccessibleProvider_iface); +} + +static HRESULT WINAPI msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider *iface, REFIID riid, void **ppv) +{ + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + return IRawElementProviderSimple_QueryInterface(&msaa_prov->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI msaa_acc_provider_AddRef(ILegacyIAccessibleProvider *iface) +{ + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + return IRawElementProviderSimple_AddRef(&msaa_prov->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI msaa_acc_provider_Release(ILegacyIAccessibleProvider *iface) +{ + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + return IRawElementProviderSimple_Release(&msaa_prov->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI msaa_acc_provider_Select(ILegacyIAccessibleProvider *iface, LONG select_flags) +{ + FIXME("%p, %#lx: stub!\n", iface, select_flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider *iface) +{ + FIXME("%p: stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_SetValue(ILegacyIAccessibleProvider *iface, LPCWSTR val) +{ + FIXME("%p, %p<%s>: stub!\n", iface, val, debugstr_w(val)); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider *iface, + IAccessible **out_acc) +{ + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + + TRACE("%p, %p\n", iface, out_acc); + + IAccessible_AddRef(msaa_prov->acc); + *out_acc = msaa_prov->acc; + + return S_OK; +} + +static HRESULT WINAPI msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider *iface, int *out_cid) +{ + struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface); + + TRACE("%p, %p\n", iface, out_cid); + *out_cid = V_I4(&msaa_prov->cid); + + return S_OK; +} + +static HRESULT WINAPI msaa_acc_provider_get_Name(ILegacyIAccessibleProvider *iface, BSTR *out_name) +{ + FIXME("%p, %p: stub!\n", iface, out_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_Value(ILegacyIAccessibleProvider *iface, BSTR *out_value) +{ + FIXME("%p, %p: stub!\n", iface, out_value); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_Description(ILegacyIAccessibleProvider *iface, + BSTR *out_description) +{ + FIXME("%p, %p: stub!\n", iface, out_description); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role) +{ + FIXME("%p, %p: stub!\n", iface, out_role); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state) +{ + FIXME("%p, %p: stub!\n", iface, out_state); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_Help(ILegacyIAccessibleProvider *iface, BSTR *out_help) +{ + FIXME("%p, %p: stub!\n", iface, out_help); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider *iface, + BSTR *out_kbd_shortcut) +{ + FIXME("%p, %p: stub!\n", iface, out_kbd_shortcut); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider *iface, + SAFEARRAY **out_selected) +{ + FIXME("%p, %p: stub!\n", iface, out_selected); + return E_NOTIMPL; +} + +static HRESULT WINAPI msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider *iface, + BSTR *out_default_action) +{ + FIXME("%p, %p: stub!\n", iface, out_default_action); + return E_NOTIMPL; +} + +static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl = { + msaa_acc_provider_QueryInterface, + msaa_acc_provider_AddRef, + msaa_acc_provider_Release, + msaa_acc_provider_Select, + msaa_acc_provider_DoDefaultAction, + msaa_acc_provider_SetValue, + msaa_acc_provider_GetIAccessible, + msaa_acc_provider_get_ChildId, + msaa_acc_provider_get_Name, + msaa_acc_provider_get_Value, + msaa_acc_provider_get_Description, + msaa_acc_provider_get_Role, + msaa_acc_provider_get_State, + msaa_acc_provider_get_Help, + msaa_acc_provider_get_KeyboardShortcut, + msaa_acc_provider_GetSelection, + msaa_acc_provider_get_DefaultAction, +}; + /*********************************************************************** * UiaProviderFromIAccessible (uiautomationcore.@) */ @@ -503,6 +665,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD return E_OUTOFMEMORY;
msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl; + msaa_prov->ILegacyIAccessibleProvider_iface.lpVtbl = &msaa_acc_provider_vtbl; msaa_prov->refcount = 1; msaa_prov->hwnd = hwnd; variant_init_i4(&msaa_prov->cid, child_id);
Huw Davies (@huw) commented about dlls/uiautomationcore/uia_provider.c:
+static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2) +{
- LONG child_count[2], left[2], top[2], width[2], height[2];
- BSTR name[2] = { NULL, NULL };
- BOOL matched = FALSE;
- int match_count = 0;
- VARIANT cid, v, v2;
- HRESULT hr, hr2;
- acc = msaa_acc_da_unwrap(acc);
- acc2 = msaa_acc_da_unwrap(acc2);
- if (acc == acc2)
- {
matched = TRUE;
goto exit;
- }
There's no need to compare the IAccessible ptrs, given that you compare their IUnknowns later.
Huw Davies (@huw) commented about dlls/uiautomationcore/uia_provider.c:
matched = TRUE;
goto exit;
- }
- else
- {
IUnknown *unk, *unk2;
IAccessible_QueryInterface(acc, &IID_IUnknown, (void**)&unk);
IUnknown_Release(unk);
IAccessible_QueryInterface(acc2, &IID_IUnknown, (void**)&unk2);
IUnknown_Release(unk2);
if (unk == unk2)
{
matched = TRUE;
goto exit;
}
Releasing the ifaces before comparing them looks odd. This is also done in the tests of the third patch.
Huw Davies (@huw) commented about dlls/uiautomationcore/uia_provider.c:
- variant_init_i4(&cid, CHILDID_SELF);
- VariantInit(&v);
- 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))
goto exit;
match_count++;
}
- }
This match_count thing looks fragile. Can't you just bail if match fails at any point?
On Thu Jun 9 13:37:43 2022 +0000, Huw Davies wrote:
Releasing the ifaces before comparing them looks odd. This is also done in the tests of the third patch.
Yeah, this is something I've borrowed from the oleacc tests, particularly the iface_cmp() function:
``` static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) { IUnknown *unk1, *unk2;
if(iface1 == iface2) return TRUE;
IUnknown_QueryInterface(iface1, &IID_IUnknown, (void**)&unk1); IUnknown_Release(unk1); IUnknown_QueryInterface(iface2, &IID_IUnknown, (void**)&unk2); IUnknown_Release(unk2); return unk1 == unk2; } ```
Does it make a difference for comparing the IUnknown pointers if the interface is released? I think in this way, it's just easier than checking, saving the check, then releasing.
This also applies to the prior `if (acc == acc2)` check you commented on, with the idea being that you can early out if the interface pointers match, and avoid a QI to try and match the IUnknown's.
On Thu Jun 9 13:37:44 2022 +0000, Huw Davies wrote:
This match_count thing looks fragile. Can't you just bail if match fails at any point?
So, an IAccessible can match based on properties can either match all four of the following properties: get_accRole get_accState get_accChildCount get_accLocation
or match get_accName. It has to match all four of the prior properties to be considered a match if get_accName doesn't return a name for both elements. We can bail if match fails at any point, which is why we go to exit.
On Thu Jun 9 13:44:28 2022 +0000, Connor McAdams wrote:
Yeah, this is something I've borrowed from the oleacc tests, particularly the iface_cmp() function:
static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) { IUnknown *unk1, *unk2; if(iface1 == iface2) return TRUE; IUnknown_QueryInterface(iface1, &IID_IUnknown, (void**)&unk1); IUnknown_Release(unk1); IUnknown_QueryInterface(iface2, &IID_IUnknown, (void**)&unk2); IUnknown_Release(unk2); return unk1 == unk2; }
Does it make a difference for comparing the IUnknown pointers if the interface is released? I think in this way, it's just easier than checking, saving the check, then releasing. This also applies to the prior `if (acc == acc2)` check you commented on, with the idea being that you can early out if the interface pointers match, and avoid a QI to try and match the IUnknown's.
It's obviously fine because you've still got a reference on the acc's, but it looks strange.
Avoiding the QI is a pointless optimization and just adds to the code size.
On Thu Jun 9 13:51:08 2022 +0000, Connor McAdams wrote:
So, an IAccessible match based on properties can either match all four of the following properties:
get_accRole get_accState get_accChildCount get_accLocation
or match get_accName. It has to match all four of the prior properties to be considered a match if get_accName doesn't return a name for both elements. We can bail if match fails at any point, which is why we go to exit.
Then probably this needs to be split into a helper function or two to make this logic clear.