Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- include/uiautomationclient.idl | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/include/uiautomationclient.idl b/include/uiautomationclient.idl index 917d3456ebf..43ac6b0752f 100644 --- a/include/uiautomationclient.idl +++ b/include/uiautomationclient.idl @@ -253,4 +253,53 @@ library UIAutomationClient { /* } */ + /* FIXME: Uncomment when properly supported in widl + [dllname("<no entry points>")] + module UIA_ControlTypeIds + { + */ + const long UIA_ButtonControlTypeId = 50000; + const long UIA_CalendarControlTypeId = 50001; + const long UIA_CheckBoxControlTypeId = 50002; + const long UIA_ComboBoxControlTypeId = 50003; + const long UIA_EditControlTypeId = 50004; + const long UIA_HyperlinkControlTypeId = 50005; + const long UIA_ImageControlTypeId = 50006; + const long UIA_ListItemControlTypeId = 50007; + const long UIA_ListControlTypeId = 50008; + const long UIA_MenuControlTypeId = 50009; + const long UIA_MenuBarControlTypeId = 50010; + const long UIA_MenuItemControlTypeId = 50011; + const long UIA_ProgressBarControlTypeId = 50012; + const long UIA_RadioButtonControlTypeId = 50013; + const long UIA_ScrollBarControlTypeId = 50014; + const long UIA_SliderControlTypeId = 50015; + const long UIA_SpinnerControlTypeId = 50016; + const long UIA_StatusBarControlTypeId = 50017; + const long UIA_TabControlTypeId = 50018; + const long UIA_TabItemControlTypeId = 50019; + const long UIA_TextControlTypeId = 50020; + const long UIA_ToolBarControlTypeId = 50021; + const long UIA_ToolTipControlTypeId = 50022; + const long UIA_TreeControlTypeId = 50023; + const long UIA_TreeItemControlTypeId = 50024; + const long UIA_CustomControlTypeId = 50025; + const long UIA_GroupControlTypeId = 50026; + const long UIA_ThumbControlTypeId = 50027; + const long UIA_DataGridControlTypeId = 50028; + const long UIA_DataItemControlTypeId = 50029; + const long UIA_DocumentControlTypeId = 50030; + const long UIA_SplitButtonControlTypeId = 50031; + const long UIA_WindowControlTypeId = 50032; + const long UIA_PaneControlTypeId = 50033; + const long UIA_HeaderControlTypeId = 50034; + const long UIA_HeaderItemControlTypeId = 50035; + const long UIA_TableControlTypeId = 50036; + const long UIA_TitleBarControlTypeId = 50037; + const long UIA_SeparatorControlTypeId = 50038; + const long UIA_SemanticZoomControlTypeId = 50039; + const long UIA_AppBarControlTypeId = 50040; + /* + }; + */ }
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 246f8aba7c1..704e43180bd 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -53,6 +53,7 @@ static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, expect_ ## func = called_ ## func = FALSE; \ }while(0)
+DEFINE_EXPECT(winproc_GETOBJECT_CLIENT); DEFINE_EXPECT(Accessible_accNavigate);
static LONG Accessible_ref = 1; @@ -60,6 +61,7 @@ static IAccessible Accessible; static IOleWindow OleWindow; static HWND Accessible_hwnd = NULL; static HWND OleWindow_hwnd = NULL; +static IAccessible *acc_client;
static BOOL check_variant_i4(VARIANT *v, int val) { @@ -130,6 +132,7 @@ 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"); return E_NOTIMPL; }
@@ -143,6 +146,7 @@ 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"); return E_NOTIMPL; }
@@ -163,12 +167,14 @@ static HRESULT WINAPI Accessible_get_accDescription(IAccessible *iface, VARIANT static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_id, VARIANT *out_role) { + ok(0, "unexpected call\n"); return E_NOTIMPL; }
static HRESULT WINAPI Accessible_get_accState(IAccessible *iface, VARIANT child_id, VARIANT *out_state) { + ok(0, "unexpected call\n"); return E_NOTIMPL; }
@@ -222,6 +228,7 @@ 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"); return E_NOTIMPL; }
@@ -341,6 +348,24 @@ static IOleWindow OleWindow = {&OleWindowVtbl};
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) + { + case WM_GETOBJECT: + if (lParam == (DWORD)OBJID_CLIENT) + { + CHECK_EXPECT(winproc_GETOBJECT_CLIENT); + if (acc_client) + return LresultFromObject(&IID_IAccessible, wParam, (IUnknown *)acc_client); + + break; + } + + break; + + default: + break; + } + return DefWindowProcA(hwnd, message, wParam, lParam); }
@@ -633,6 +658,8 @@ static void test_UiaProviderFromIAccessible(void)
/* Return an HWND from accNavigate, not OleWindow. */ SET_EXPECT(Accessible_accNavigate); + SET_EXPECT(winproc_GETOBJECT_CLIENT); + acc_client = &Accessible; Accessible_hwnd = hwnd; OleWindow_hwnd = NULL; hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); @@ -641,6 +668,15 @@ static void test_UiaProviderFromIAccessible(void) ok(Accessible_ref == 2, "Unexpected refcnt %ld\n", Accessible_ref); IRawElementProviderSimple_Release(elprov); ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref); + acc_client = NULL; + + /* Skip tests on Win10v1507. */ + if (called_winproc_GETOBJECT_CLIENT) + { + win_skip("UiaProviderFromIAccessible behaves inconsistently on Win10 1507, skipping tests.\n"); + return; + } + expect_winproc_GETOBJECT_CLIENT = FALSE;
/* Return an HWND from OleWindow, not accNavigate. */ Accessible_hwnd = NULL;
On Wed, May 25, 2022 at 02:37:35PM -0400, Connor McAdams wrote:
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Could we write the tests so that they don't rely on implementation details? Presumably applications run correctly on Windows 10 v1507.
Huw.
On Tue, Jun 07, 2022 at 10:07:22AM +0100, Huw Davies wrote:
On Wed, May 25, 2022 at 02:37:35PM -0400, Connor McAdams wrote:
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Could we write the tests so that they don't rely on implementation details? Presumably applications run correctly on Windows 10 v1507.
Huw.
So, essentially most of my tests for UI Automation kind of revolve around things like SET_EXPECT/CHECK_EXPECT/CHECK_CALLED tests as a way of testing what UI Automation does with the interfaces we pass into it. This is similar to how the tests for oleacc are done. In this case, we'd end up with a number of tests where we'd have to add a bunch of SET_EXPECT's to account for one specific version of Windows, then we'd need some sort of special CHECK_CALLED that could account for a broken behavior. It's a lot more work than I think it's worth to work around a bug in one specific version of Windows 10.
We could also just remove the whole checking of what methods are called in the tests, but most of my UI Automation tests rely upon this.
Thanks for the review. :)
On Tue, Jun 07, 2022 at 11:02:31AM -0400, Connor McAdams wrote:
On Tue, Jun 07, 2022 at 10:07:22AM +0100, Huw Davies wrote:
On Wed, May 25, 2022 at 02:37:35PM -0400, Connor McAdams wrote:
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Could we write the tests so that they don't rely on implementation details? Presumably applications run correctly on Windows 10 v1507.
Huw.
So, essentially most of my tests for UI Automation kind of revolve around things like SET_EXPECT/CHECK_EXPECT/CHECK_CALLED tests as a way of testing what UI Automation does with the interfaces we pass into it. This is similar to how the tests for oleacc are done. In this case, we'd end up with a number of tests where we'd have to add a bunch of SET_EXPECT's to account for one specific version of Windows, then we'd need some sort of special CHECK_CALLED that could account for a broken behavior. It's a lot more work than I think it's worth to work around a bug in one specific version of Windows 10.
Is it really a bug in Windows; it seems more like a different implementation? How different are the call sequences?
Huw.
On Tue, Jun 07, 2022 at 04:13:45PM +0100, Huw Davies wrote:
On Tue, Jun 07, 2022 at 11:02:31AM -0400, Connor McAdams wrote:
On Tue, Jun 07, 2022 at 10:07:22AM +0100, Huw Davies wrote:
On Wed, May 25, 2022 at 02:37:35PM -0400, Connor McAdams wrote:
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Could we write the tests so that they don't rely on implementation details? Presumably applications run correctly on Windows 10 v1507.
Huw.
So, essentially most of my tests for UI Automation kind of revolve around things like SET_EXPECT/CHECK_EXPECT/CHECK_CALLED tests as a way of testing what UI Automation does with the interfaces we pass into it. This is similar to how the tests for oleacc are done. In this case, we'd end up with a number of tests where we'd have to add a bunch of SET_EXPECT's to account for one specific version of Windows, then we'd need some sort of special CHECK_CALLED that could account for a broken behavior. It's a lot more work than I think it's worth to work around a bug in one specific version of Windows 10.
Is it really a bug in Windows; it seems more like a different implementation? How different are the call sequences?
Huw.
I believe it's a bug, or at least a behavior that was undesirable. All other versions of Windows seem to delay this root IAccessible check until specific methods are called on the IRawElementProviderSimple wrapper. The check is basically sending the HWND associated with an IAccessible a WM_GETOBJECT message, and then the returned IAccessible is compared against the currently wrapped one. This can end up with:
SET_EXPECT(Accessible_get_accRole); SET_EXPECT(Accessible_get_accState); SET_EXPECT(Accessible_get_accChildCount); SET_EXPECT(Accessible_accLocation); SET_EXPECT(Accessible_get_accName);
On both interfaces. This also causes issues when testing the methods that trigger the same check on other versions of Windows, because if the check is done immediately when UiaProviderFromIAccessible is called, the check isn't done on the method calls, meaning we again have to work around this.
On Tue, Jun 07, 2022 at 11:29:12AM -0400, Connor McAdams wrote:
On Tue, Jun 07, 2022 at 04:13:45PM +0100, Huw Davies wrote:
On Tue, Jun 07, 2022 at 11:02:31AM -0400, Connor McAdams wrote:
On Tue, Jun 07, 2022 at 10:07:22AM +0100, Huw Davies wrote:
On Wed, May 25, 2022 at 02:37:35PM -0400, Connor McAdams wrote:
In Windows 10 version 1507, UiaProviderFromIAccessible will try to check the IAccessible passed in against the root IAccessible of the HWND associated with the passed in IAccessible. In all other Windows versions, this check is done on a call to get_HostRawElementProvider, or the first time navigating towards a parent or sibling. Rather than work around this, just skip the tests if this happens.
Could we write the tests so that they don't rely on implementation details? Presumably applications run correctly on Windows 10 v1507.
Huw.
So, essentially most of my tests for UI Automation kind of revolve around things like SET_EXPECT/CHECK_EXPECT/CHECK_CALLED tests as a way of testing what UI Automation does with the interfaces we pass into it. This is similar to how the tests for oleacc are done. In this case, we'd end up with a number of tests where we'd have to add a bunch of SET_EXPECT's to account for one specific version of Windows, then we'd need some sort of special CHECK_CALLED that could account for a broken behavior. It's a lot more work than I think it's worth to work around a bug in one specific version of Windows 10.
Is it really a bug in Windows; it seems more like a different implementation? How different are the call sequences?
I believe it's a bug, or at least a behavior that was undesirable. All other versions of Windows seem to delay this root IAccessible check until specific methods are called on the IRawElementProviderSimple wrapper. The check is basically sending the HWND associated with an IAccessible a WM_GETOBJECT message, and then the returned IAccessible is compared against the currently wrapped one. This can end up with:
I'm still not convinced it's a bug or even undesirable behaviour; it's just different. However, skipping here does allow some useful tests, so I'll let it go and hope Windows doesn't change its implementation again.
Huw.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/Makefile.in | 2 +- dlls/uiautomationcore/tests/uiautomation.c | 64 +++++++++++++++++++--- dlls/uiautomationcore/uia_provider.c | 28 ++-------- 3 files changed, 61 insertions(+), 33 deletions(-)
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 61a560d6c2e..a1cf5575814 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -1,6 +1,6 @@ MODULE = uiautomationcore.dll IMPORTLIB = uiautomationcore -IMPORTS = uuid ole32 oleaut32 user32 +IMPORTS = uuid ole32 oleaut32 user32 oleacc
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 704e43180bd..344fd2cdbf0 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -53,11 +53,18 @@ static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, expect_ ## func = called_ ## func = FALSE; \ }while(0)
+#define NAVDIR_INTERNAL_HWND 10 + DEFINE_EXPECT(winproc_GETOBJECT_CLIENT); DEFINE_EXPECT(Accessible_accNavigate); +DEFINE_EXPECT(Accessible_get_accParent); +DEFINE_EXPECT(Accessible_child_accNavigate); +DEFINE_EXPECT(Accessible_child_get_accParent);
static LONG Accessible_ref = 1; +static LONG Accessible_child_ref = 1; static IAccessible Accessible; +static IAccessible Accessible_child; static IOleWindow OleWindow; static HWND Accessible_hwnd = NULL; static HWND OleWindow_hwnd = NULL; @@ -77,7 +84,7 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid, if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_IAccessible)) *obj = iface; - else if (IsEqualIID(riid, &IID_IOleWindow)) + else if (IsEqualIID(riid, &IID_IOleWindow) && (iface == &Accessible)) *obj = &OleWindow; else return E_NOINTERFACE; @@ -88,12 +95,18 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid,
static ULONG WINAPI Accessible_AddRef(IAccessible *iface) { - return InterlockedIncrement(&Accessible_ref); + if (iface == &Accessible_child) + return InterlockedIncrement(&Accessible_child_ref); + else + return InterlockedIncrement(&Accessible_ref); }
static ULONG WINAPI Accessible_Release(IAccessible *iface) { - return InterlockedDecrement(&Accessible_ref); + if (iface == &Accessible_child) + return InterlockedDecrement(&Accessible_child_ref); + else + return InterlockedDecrement(&Accessible_ref); }
static HRESULT WINAPI Accessible_GetTypeInfoCount(IAccessible *iface, UINT *pctinfo) @@ -126,8 +139,16 @@ static HRESULT WINAPI Accessible_Invoke(IAccessible *iface, DISPID disp_id_membe
static HRESULT WINAPI Accessible_get_accParent(IAccessible *iface, IDispatch **out_parent) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + if (iface == &Accessible_child) + { + CHECK_EXPECT(Accessible_child_get_accParent); + return IAccessible_QueryInterface(&Accessible, &IID_IDispatch, (void **)out_parent); + } + else + CHECK_EXPECT(Accessible_get_accParent); + + *out_parent = NULL; + return S_FALSE; }
static HRESULT WINAPI Accessible_get_accChildCount(IAccessible *iface, LONG *out_count) @@ -235,14 +256,18 @@ static HRESULT WINAPI Accessible_accLocation(IAccessible *iface, LONG *out_left, static HRESULT WINAPI Accessible_accNavigate(IAccessible *iface, LONG nav_direction, VARIANT child_id_start, VARIANT *out_var) { - CHECK_EXPECT(Accessible_accNavigate); + if (iface == &Accessible_child) + CHECK_EXPECT(Accessible_child_accNavigate); + else + CHECK_EXPECT(Accessible_accNavigate); VariantInit(out_var);
/* * This is an undocumented way for UI Automation to get an HWND for * IAccessible's contained in a Direct Annotation wrapper object. */ - if ((nav_direction == 10) && check_variant_i4(&child_id_start, CHILDID_SELF)) + if ((nav_direction == NAVDIR_INTERNAL_HWND) && check_variant_i4(&child_id_start, CHILDID_SELF) && + Accessible_hwnd) { V_VT(out_var) = VT_I4; V_I4(out_var) = HandleToUlong(Accessible_hwnd); @@ -326,8 +351,13 @@ static ULONG WINAPI OleWindow_Release(IOleWindow *iface)
static HRESULT WINAPI OleWindow_GetWindow(IOleWindow *iface, HWND *hwnd) { - *hwnd = OleWindow_hwnd; - return S_OK; + if (OleWindow_hwnd) + { + *hwnd = OleWindow_hwnd; + return S_OK; + } + + return E_FAIL; }
static HRESULT WINAPI OleWindow_ContextSensitiveHelp(IOleWindow *iface, BOOL f_enter_mode) @@ -344,6 +374,7 @@ static const IOleWindowVtbl OleWindowVtbl = { };
static IAccessible Accessible = {&AccessibleVtbl}; +static IAccessible Accessible_child = {&AccessibleVtbl}; static IOleWindow OleWindow = {&OleWindowVtbl};
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -650,11 +681,13 @@ static void test_UiaProviderFromIAccessible(void)
/* Don't return an HWND from accNavigate or OleWindow. */ SET_EXPECT(Accessible_accNavigate); + SET_EXPECT(Accessible_get_accParent); Accessible_hwnd = NULL; OleWindow_hwnd = NULL; hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(Accessible_accNavigate); + CHECK_CALLED(Accessible_get_accParent);
/* Return an HWND from accNavigate, not OleWindow. */ SET_EXPECT(Accessible_accNavigate); @@ -678,6 +711,19 @@ static void test_UiaProviderFromIAccessible(void) } expect_winproc_GETOBJECT_CLIENT = FALSE;
+ /* Return an HWND from parent IAccessible's IOleWindow interface. */ + SET_EXPECT(Accessible_child_accNavigate); + SET_EXPECT(Accessible_child_get_accParent); + Accessible_hwnd = NULL; + OleWindow_hwnd = hwnd; + hr = pUiaProviderFromIAccessible(&Accessible_child, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(Accessible_child_accNavigate); + CHECK_CALLED(Accessible_child_get_accParent); + ok(Accessible_child_ref == 2, "Unexpected refcnt %ld\n", Accessible_child_ref); + IRawElementProviderSimple_Release(elprov); + ok(Accessible_child_ref == 1, "Unexpected refcnt %ld\n", Accessible_child_ref); + /* Return an HWND from OleWindow, not accNavigate. */ Accessible_hwnd = NULL; OleWindow_hwnd = hwnd; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 790593dbcab..d2a0da15c3c 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -151,7 +151,6 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD struct msaa_provider *msaa_prov; IServiceProvider *serv_prov; HWND hwnd = NULL; - IOleWindow *win; HRESULT hr;
TRACE("(%p, %ld, %#lx, %p)\n", acc, child_id, flags, elprov); @@ -187,28 +186,11 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD IServiceProvider_Release(serv_prov); }
- hr = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void **)&win); - if (SUCCEEDED(hr)) - { - hr = IOleWindow_GetWindow(win, &hwnd); - if (FAILED(hr)) - hwnd = NULL; - IOleWindow_Release(win); - } - - if (!IsWindow(hwnd)) - { - VARIANT v, cid; - - VariantInit(&v); - variant_init_i4(&cid, CHILDID_SELF); - hr = IAccessible_accNavigate(acc, 10, cid, &v); - if (SUCCEEDED(hr) && V_VT(&v) == VT_I4) - hwnd = ULongToHandle(V_I4(&v)); - - if (!IsWindow(hwnd)) - return E_FAIL; - } + hr = WindowFromAccessibleObject(acc, &hwnd); + if (FAILED(hr)) + return hr; + if (!hwnd) + return E_FAIL;
msaa_prov = heap_alloc(sizeof(*msaa_prov)); if (!msaa_prov)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 151 ++++++++++++--------- 1 file changed, 90 insertions(+), 61 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 344fd2cdbf0..f61a4b21c50 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -61,13 +61,6 @@ DEFINE_EXPECT(Accessible_get_accParent); DEFINE_EXPECT(Accessible_child_accNavigate); DEFINE_EXPECT(Accessible_child_get_accParent);
-static LONG Accessible_ref = 1; -static LONG Accessible_child_ref = 1; -static IAccessible Accessible; -static IAccessible Accessible_child; -static IOleWindow OleWindow; -static HWND Accessible_hwnd = NULL; -static HWND OleWindow_hwnd = NULL; static IAccessible *acc_client;
static BOOL check_variant_i4(VARIANT *v, int val) @@ -78,14 +71,32 @@ static BOOL check_variant_i4(VARIANT *v, int val) return FALSE; }
+static struct Accessible +{ + IAccessible IAccessible_iface; + IOleWindow IOleWindow_iface; + LONG ref; + + IAccessible *parent; + HWND acc_hwnd; + HWND ow_hwnd; +} Accessible, Accessible_child; + +static inline struct Accessible* impl_from_Accessible(IAccessible *iface) +{ + return CONTAINING_RECORD(iface, struct Accessible, IAccessible_iface); +} + static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid, void **obj) { + struct Accessible *This = impl_from_Accessible(iface); + *obj = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_IAccessible)) *obj = iface; - else if (IsEqualIID(riid, &IID_IOleWindow) && (iface == &Accessible)) - *obj = &OleWindow; + else if (IsEqualIID(riid, &IID_IOleWindow)) + *obj = &This->IOleWindow_iface; else return E_NOINTERFACE;
@@ -95,18 +106,14 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid,
static ULONG WINAPI Accessible_AddRef(IAccessible *iface) { - if (iface == &Accessible_child) - return InterlockedIncrement(&Accessible_child_ref); - else - return InterlockedIncrement(&Accessible_ref); + struct Accessible *This = impl_from_Accessible(iface); + return InterlockedIncrement(&This->ref); }
static ULONG WINAPI Accessible_Release(IAccessible *iface) { - if (iface == &Accessible_child) - return InterlockedDecrement(&Accessible_child_ref); - else - return InterlockedDecrement(&Accessible_ref); + struct Accessible *This = impl_from_Accessible(iface); + return InterlockedDecrement(&This->ref); }
static HRESULT WINAPI Accessible_GetTypeInfoCount(IAccessible *iface, UINT *pctinfo) @@ -139,14 +146,16 @@ static HRESULT WINAPI Accessible_Invoke(IAccessible *iface, DISPID disp_id_membe
static HRESULT WINAPI Accessible_get_accParent(IAccessible *iface, IDispatch **out_parent) { - if (iface == &Accessible_child) - { + struct Accessible *This = impl_from_Accessible(iface); + + if (This == &Accessible_child) CHECK_EXPECT(Accessible_child_get_accParent); - return IAccessible_QueryInterface(&Accessible, &IID_IDispatch, (void **)out_parent); - } else CHECK_EXPECT(Accessible_get_accParent);
+ if (This->parent) + return IAccessible_QueryInterface(This->parent, &IID_IDispatch, (void **)out_parent); + *out_parent = NULL; return S_FALSE; } @@ -256,7 +265,9 @@ static HRESULT WINAPI Accessible_accLocation(IAccessible *iface, LONG *out_left, static HRESULT WINAPI Accessible_accNavigate(IAccessible *iface, LONG nav_direction, VARIANT child_id_start, VARIANT *out_var) { - if (iface == &Accessible_child) + struct Accessible *This = impl_from_Accessible(iface); + + if (This == &Accessible_child) CHECK_EXPECT(Accessible_child_accNavigate); else CHECK_EXPECT(Accessible_accNavigate); @@ -267,10 +278,10 @@ static HRESULT WINAPI Accessible_accNavigate(IAccessible *iface, LONG nav_direct * IAccessible's contained in a Direct Annotation wrapper object. */ if ((nav_direction == NAVDIR_INTERNAL_HWND) && check_variant_i4(&child_id_start, CHILDID_SELF) && - Accessible_hwnd) + This->acc_hwnd) { V_VT(out_var) = VT_I4; - V_I4(out_var) = HandleToUlong(Accessible_hwnd); + V_I4(out_var) = HandleToUlong(This->acc_hwnd); return S_OK; } return S_FALSE; @@ -334,30 +345,35 @@ static IAccessibleVtbl AccessibleVtbl = { Accessible_put_accValue };
+static inline struct Accessible* impl_from_OleWindow(IOleWindow *iface) +{ + return CONTAINING_RECORD(iface, struct Accessible, IOleWindow_iface); +} + static HRESULT WINAPI OleWindow_QueryInterface(IOleWindow *iface, REFIID riid, void **obj) { - return IAccessible_QueryInterface(&Accessible, riid, obj); + struct Accessible *This = impl_from_OleWindow(iface); + return IAccessible_QueryInterface(&This->IAccessible_iface, riid, obj); }
static ULONG WINAPI OleWindow_AddRef(IOleWindow *iface) { - return IAccessible_AddRef(&Accessible); + struct Accessible *This = impl_from_OleWindow(iface); + return IAccessible_AddRef(&This->IAccessible_iface); }
static ULONG WINAPI OleWindow_Release(IOleWindow *iface) { - return IAccessible_Release(&Accessible); + struct Accessible *This = impl_from_OleWindow(iface); + return IAccessible_Release(&This->IAccessible_iface); }
static HRESULT WINAPI OleWindow_GetWindow(IOleWindow *iface, HWND *hwnd) { - if (OleWindow_hwnd) - { - *hwnd = OleWindow_hwnd; - return S_OK; - } + struct Accessible *This = impl_from_OleWindow(iface);
- return E_FAIL; + *hwnd = This->ow_hwnd; + return *hwnd ? S_OK : E_FAIL; }
static HRESULT WINAPI OleWindow_ContextSensitiveHelp(IOleWindow *iface, BOOL f_enter_mode) @@ -373,9 +389,22 @@ static const IOleWindowVtbl OleWindowVtbl = { OleWindow_ContextSensitiveHelp };
-static IAccessible Accessible = {&AccessibleVtbl}; -static IAccessible Accessible_child = {&AccessibleVtbl}; -static IOleWindow OleWindow = {&OleWindowVtbl}; +static struct Accessible Accessible = +{ + { &AccessibleVtbl }, + { &OleWindowVtbl }, + 1, + NULL, + 0, 0 +}; +static struct Accessible Accessible_child = +{ + { &AccessibleVtbl }, + { &OleWindowVtbl }, + 1, + &Accessible.IAccessible_iface, + 0, 0 +};
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -663,7 +692,7 @@ static void test_UiaProviderFromIAccessible(void) hr = pUiaProviderFromIAccessible(NULL, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
- hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, NULL); + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, NULL); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
/* @@ -682,9 +711,9 @@ static void test_UiaProviderFromIAccessible(void) /* Don't return an HWND from accNavigate or OleWindow. */ SET_EXPECT(Accessible_accNavigate); SET_EXPECT(Accessible_get_accParent); - Accessible_hwnd = NULL; - OleWindow_hwnd = NULL; - hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + Accessible.acc_hwnd = NULL; + Accessible.ow_hwnd = NULL; + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(Accessible_accNavigate); CHECK_CALLED(Accessible_get_accParent); @@ -692,15 +721,15 @@ static void test_UiaProviderFromIAccessible(void) /* Return an HWND from accNavigate, not OleWindow. */ SET_EXPECT(Accessible_accNavigate); SET_EXPECT(winproc_GETOBJECT_CLIENT); - acc_client = &Accessible; - Accessible_hwnd = hwnd; - OleWindow_hwnd = NULL; - hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + acc_client = &Accessible.IAccessible_iface; + Accessible.acc_hwnd = hwnd; + Accessible.ow_hwnd = NULL; + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(Accessible_accNavigate); - ok(Accessible_ref == 2, "Unexpected refcnt %ld\n", Accessible_ref); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); IRawElementProviderSimple_Release(elprov); - ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); acc_client = NULL;
/* Skip tests on Win10v1507. */ @@ -714,22 +743,22 @@ static void test_UiaProviderFromIAccessible(void) /* Return an HWND from parent IAccessible's IOleWindow interface. */ SET_EXPECT(Accessible_child_accNavigate); SET_EXPECT(Accessible_child_get_accParent); - Accessible_hwnd = NULL; - OleWindow_hwnd = hwnd; - hr = pUiaProviderFromIAccessible(&Accessible_child, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + Accessible.acc_hwnd = NULL; + Accessible.ow_hwnd = hwnd; + hr = pUiaProviderFromIAccessible(&Accessible_child.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(Accessible_child_accNavigate); CHECK_CALLED(Accessible_child_get_accParent); - ok(Accessible_child_ref == 2, "Unexpected refcnt %ld\n", Accessible_child_ref); + ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref); IRawElementProviderSimple_Release(elprov); - ok(Accessible_child_ref == 1, "Unexpected refcnt %ld\n", Accessible_child_ref); + ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
/* Return an HWND from OleWindow, not accNavigate. */ - Accessible_hwnd = NULL; - OleWindow_hwnd = hwnd; - hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + Accessible.acc_hwnd = NULL; + Accessible.ow_hwnd = hwnd; + 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); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opt); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -743,19 +772,19 @@ static void test_UiaProviderFromIAccessible(void) VariantClear(&v);
IRawElementProviderSimple_Release(elprov); - ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
/* ChildID other than CHILDID_SELF. */ - hr = pUiaProviderFromIAccessible(&Accessible, 1, UIA_PFIA_DEFAULT, &elprov); + 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); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); IRawElementProviderSimple_Release(elprov); - ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
DestroyWindow(hwnd); UnregisterClassA("pUiaProviderFromIAccessible class", NULL); - Accessible_hwnd = NULL; - OleWindow_hwnd = NULL; + Accessible.acc_hwnd = NULL; + Accessible.ow_hwnd = NULL; }
START_TEST(uiautomation)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 170 ++++++++++++++++++++- dlls/uiautomationcore/uia_provider.c | 100 +++++++++++- 2 files changed, 266 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f61a4b21c50..c735e7a0bd5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -58,6 +58,7 @@ static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, DEFINE_EXPECT(winproc_GETOBJECT_CLIENT); DEFINE_EXPECT(Accessible_accNavigate); DEFINE_EXPECT(Accessible_get_accParent); +DEFINE_EXPECT(Accessible_get_accRole); DEFINE_EXPECT(Accessible_child_accNavigate); DEFINE_EXPECT(Accessible_child_get_accParent);
@@ -80,6 +81,7 @@ static struct Accessible IAccessible *parent; HWND acc_hwnd; HWND ow_hwnd; + INT role; } Accessible, Accessible_child;
static inline struct Accessible* impl_from_Accessible(IAccessible *iface) @@ -197,7 +199,18 @@ static HRESULT WINAPI Accessible_get_accDescription(IAccessible *iface, VARIANT static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_id, VARIANT *out_role) { - ok(0, "unexpected call\n"); + struct Accessible *This = impl_from_Accessible(iface); + + ok(This == &Accessible, "unexpected call\n"); + CHECK_EXPECT(Accessible_get_accRole); + + if (This->role) + { + V_VT(out_role) = VT_I4; + V_I4(out_role) = This->role; + return S_OK; + } + return E_NOTIMPL; }
@@ -395,7 +408,8 @@ static struct Accessible Accessible = { &OleWindowVtbl }, 1, NULL, - 0, 0 + 0, 0, + 0, }; static struct Accessible Accessible_child = { @@ -403,7 +417,8 @@ static struct Accessible Accessible_child = { &OleWindowVtbl }, 1, &Accessible.IAccessible_iface, - 0, 0 + 0, 0, + 0, };
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -662,6 +677,153 @@ static void test_uia_reserved_value_ifaces(void) CoUninitialize(); }
+struct msaa_role_uia_type { + INT acc_role; + INT uia_control_type; +}; + +static const struct msaa_role_uia_type msaa_role_uia_types[] = { + { ROLE_SYSTEM_TITLEBAR, UIA_TitleBarControlTypeId }, + { ROLE_SYSTEM_MENUBAR, UIA_MenuBarControlTypeId }, + { ROLE_SYSTEM_SCROLLBAR, UIA_ScrollBarControlTypeId }, + { ROLE_SYSTEM_GRIP, UIA_ThumbControlTypeId }, + { ROLE_SYSTEM_WINDOW, UIA_WindowControlTypeId }, + { ROLE_SYSTEM_MENUPOPUP, UIA_MenuControlTypeId }, + { ROLE_SYSTEM_MENUITEM, UIA_MenuItemControlTypeId }, + { ROLE_SYSTEM_TOOLTIP, UIA_ToolTipControlTypeId }, + { ROLE_SYSTEM_APPLICATION, UIA_WindowControlTypeId }, + { ROLE_SYSTEM_DOCUMENT, UIA_DocumentControlTypeId }, + { ROLE_SYSTEM_PANE, UIA_PaneControlTypeId }, + { ROLE_SYSTEM_GROUPING, UIA_GroupControlTypeId }, + { ROLE_SYSTEM_SEPARATOR, UIA_SeparatorControlTypeId }, + { ROLE_SYSTEM_TOOLBAR, UIA_ToolBarControlTypeId }, + { ROLE_SYSTEM_STATUSBAR, UIA_StatusBarControlTypeId }, + { ROLE_SYSTEM_TABLE, UIA_TableControlTypeId }, + { ROLE_SYSTEM_COLUMNHEADER, UIA_HeaderControlTypeId }, + { ROLE_SYSTEM_ROWHEADER, UIA_HeaderControlTypeId }, + { ROLE_SYSTEM_CELL, UIA_DataItemControlTypeId }, + { ROLE_SYSTEM_LINK, UIA_HyperlinkControlTypeId }, + { ROLE_SYSTEM_LIST, UIA_ListControlTypeId }, + { ROLE_SYSTEM_LISTITEM, UIA_ListItemControlTypeId }, + { ROLE_SYSTEM_OUTLINE, UIA_TreeControlTypeId }, + { ROLE_SYSTEM_OUTLINEITEM, UIA_TreeItemControlTypeId }, + { ROLE_SYSTEM_PAGETAB, UIA_TabItemControlTypeId }, + { ROLE_SYSTEM_INDICATOR, UIA_ThumbControlTypeId }, + { ROLE_SYSTEM_GRAPHIC, UIA_ImageControlTypeId }, + { ROLE_SYSTEM_STATICTEXT, UIA_TextControlTypeId }, + { ROLE_SYSTEM_TEXT, UIA_EditControlTypeId }, + { ROLE_SYSTEM_PUSHBUTTON, UIA_ButtonControlTypeId }, + { ROLE_SYSTEM_CHECKBUTTON, UIA_CheckBoxControlTypeId }, + { ROLE_SYSTEM_RADIOBUTTON, UIA_RadioButtonControlTypeId }, + { ROLE_SYSTEM_COMBOBOX, UIA_ComboBoxControlTypeId }, + { ROLE_SYSTEM_PROGRESSBAR, UIA_ProgressBarControlTypeId }, + { ROLE_SYSTEM_SLIDER, UIA_SliderControlTypeId }, + { ROLE_SYSTEM_SPINBUTTON, UIA_SpinnerControlTypeId }, + { ROLE_SYSTEM_BUTTONDROPDOWN, UIA_SplitButtonControlTypeId }, + { ROLE_SYSTEM_BUTTONMENU, UIA_MenuItemControlTypeId }, + { ROLE_SYSTEM_BUTTONDROPDOWNGRID, UIA_ButtonControlTypeId }, + { ROLE_SYSTEM_PAGETABLIST, UIA_TabControlTypeId }, + { ROLE_SYSTEM_SPLITBUTTON, UIA_SplitButtonControlTypeId }, + /* These accessible roles have no equivalent in UI Automation. */ + { ROLE_SYSTEM_SOUND, 0 }, + { ROLE_SYSTEM_CURSOR, 0 }, + { ROLE_SYSTEM_CARET, 0 }, + { ROLE_SYSTEM_ALERT, 0 }, + { ROLE_SYSTEM_CLIENT, 0 }, + { ROLE_SYSTEM_CHART, 0 }, + { ROLE_SYSTEM_DIALOG, 0 }, + { ROLE_SYSTEM_BORDER, 0 }, + { ROLE_SYSTEM_COLUMN, 0 }, + { ROLE_SYSTEM_ROW, 0 }, + { ROLE_SYSTEM_HELPBALLOON, 0 }, + { ROLE_SYSTEM_CHARACTER, 0 }, + { ROLE_SYSTEM_PROPERTYPAGE, 0 }, + { ROLE_SYSTEM_DROPLIST, 0 }, + { ROLE_SYSTEM_DIAL, 0 }, + { ROLE_SYSTEM_HOTKEYFIELD, 0 }, + { ROLE_SYSTEM_DIAGRAM, 0 }, + { ROLE_SYSTEM_ANIMATION, 0 }, + { ROLE_SYSTEM_EQUATION, 0 }, + { ROLE_SYSTEM_WHITESPACE, 0 }, + { ROLE_SYSTEM_IPADDRESS, 0 }, + { ROLE_SYSTEM_OUTLINEBUTTON, 0 }, +}; + +static void test_uia_prov_from_acc_properties(void) +{ + IRawElementProviderSimple *elprov; + HRESULT hr; + VARIANT v; + int i; + + /* MSAA role to UIA control type test. */ + for (i = 0; i < ARRAY_SIZE(msaa_role_uia_types); i++) + { + const struct msaa_role_uia_type *role = &msaa_role_uia_types[i]; + + /* + * Roles get cached once a valid one is mapped, so create a new + * element for each role. + */ + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + Accessible.role = role->acc_role; + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (role->uia_control_type) + ok(check_variant_i4(&v, role->uia_control_type), "MSAA role %d: V_I4(&v) = %ld\n", role->acc_role, V_I4(&v)); + else + ok(V_VT(&v) == VT_EMPTY, "MSAA role %d: V_VT(&v) = %d\n", role->acc_role, V_VT(&v)); + CHECK_CALLED(Accessible_get_accRole); + + if (!role->uia_control_type) + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (role->uia_control_type) + ok(check_variant_i4(&v, role->uia_control_type), "MSAA role %d: V_I4(&v) = %ld\n", role->acc_role, V_I4(&v)); + else + ok(V_VT(&v) == VT_EMPTY, "MSAA role %d: V_VT(&v) = %d\n", role->acc_role, V_VT(&v)); + if (!role->uia_control_type) + CHECK_CALLED(Accessible_get_accRole); + + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + } + + /* ROLE_SYSTEM_CLOCK has no mapping in Windows < 10 1809. */ + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + Accessible.role = ROLE_SYSTEM_CLOCK; + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_i4(&v, UIA_ButtonControlTypeId) || broken(V_VT(&v) == VT_EMPTY), /* Windows < 10 1809 */ + "MSAA role %d: V_I4(&v) = %ld\n", Accessible.role, V_I4(&v)); + CHECK_CALLED(Accessible_get_accRole); + + if (V_VT(&v) == VT_EMPTY) + SET_EXPECT(Accessible_get_accRole); + VariantClear(&v); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ControlTypePropertyId, &v); + ok(check_variant_i4(&v, UIA_ButtonControlTypeId) || broken(V_VT(&v) == VT_EMPTY), /* Windows < 10 1809 */ + "MSAA role %d: V_I4(&v) = %ld\n", Accessible.role, V_I4(&v)); + if (V_VT(&v) == VT_EMPTY) + CHECK_CALLED(Accessible_get_accRole); + + Accessible.role = 0; + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); +} + static void test_UiaProviderFromIAccessible(void) { IRawElementProviderSimple *elprov; @@ -781,6 +943,8 @@ static void test_UiaProviderFromIAccessible(void) IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
+ test_uia_prov_from_acc_properties(); + DestroyWindow(hwnd); UnregisterClassA("pUiaProviderFromIAccessible class", NULL); Accessible.acc_hwnd = NULL; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index d2a0da15c3c..ab2b8305b10 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -32,6 +32,85 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static LONG msaa_role_to_uia_control_type(LONG role) +{ + switch (role) + { + case ROLE_SYSTEM_TITLEBAR: return UIA_TitleBarControlTypeId; + case ROLE_SYSTEM_MENUBAR: return UIA_MenuBarControlTypeId; + case ROLE_SYSTEM_SCROLLBAR: return UIA_ScrollBarControlTypeId; + case ROLE_SYSTEM_INDICATOR: + case ROLE_SYSTEM_GRIP: return UIA_ThumbControlTypeId; + case ROLE_SYSTEM_APPLICATION: + case ROLE_SYSTEM_WINDOW: return UIA_WindowControlTypeId; + case ROLE_SYSTEM_MENUPOPUP: return UIA_MenuControlTypeId; + case ROLE_SYSTEM_TOOLTIP: return UIA_ToolTipControlTypeId; + case ROLE_SYSTEM_DOCUMENT: return UIA_DocumentControlTypeId; + case ROLE_SYSTEM_PANE: return UIA_PaneControlTypeId; + case ROLE_SYSTEM_GROUPING: return UIA_GroupControlTypeId; + case ROLE_SYSTEM_SEPARATOR: return UIA_SeparatorControlTypeId; + case ROLE_SYSTEM_TOOLBAR: return UIA_ToolBarControlTypeId; + case ROLE_SYSTEM_STATUSBAR: return UIA_StatusBarControlTypeId; + case ROLE_SYSTEM_TABLE: return UIA_TableControlTypeId; + case ROLE_SYSTEM_COLUMNHEADER: + case ROLE_SYSTEM_ROWHEADER: return UIA_HeaderControlTypeId; + case ROLE_SYSTEM_CELL: return UIA_DataItemControlTypeId; + case ROLE_SYSTEM_LINK: return UIA_HyperlinkControlTypeId; + case ROLE_SYSTEM_LIST: return UIA_ListControlTypeId; + case ROLE_SYSTEM_LISTITEM: return UIA_ListItemControlTypeId; + case ROLE_SYSTEM_OUTLINE: return UIA_TreeControlTypeId; + case ROLE_SYSTEM_OUTLINEITEM: return UIA_TreeItemControlTypeId; + case ROLE_SYSTEM_PAGETAB: return UIA_TabItemControlTypeId; + case ROLE_SYSTEM_GRAPHIC: return UIA_ImageControlTypeId; + case ROLE_SYSTEM_STATICTEXT: return UIA_TextControlTypeId; + case ROLE_SYSTEM_TEXT: return UIA_EditControlTypeId; + case ROLE_SYSTEM_CLOCK: + case ROLE_SYSTEM_BUTTONDROPDOWNGRID: + case ROLE_SYSTEM_PUSHBUTTON: return UIA_ButtonControlTypeId; + case ROLE_SYSTEM_CHECKBUTTON: return UIA_CheckBoxControlTypeId; + case ROLE_SYSTEM_RADIOBUTTON: return UIA_RadioButtonControlTypeId; + case ROLE_SYSTEM_COMBOBOX: return UIA_ComboBoxControlTypeId; + case ROLE_SYSTEM_PROGRESSBAR: return UIA_ProgressBarControlTypeId; + case ROLE_SYSTEM_SLIDER: return UIA_SliderControlTypeId; + case ROLE_SYSTEM_SPINBUTTON: return UIA_SpinnerControlTypeId; + case ROLE_SYSTEM_BUTTONMENU: + case ROLE_SYSTEM_MENUITEM: return UIA_MenuItemControlTypeId; + case ROLE_SYSTEM_PAGETABLIST: return UIA_TabControlTypeId; + case ROLE_SYSTEM_BUTTONDROPDOWN: + case ROLE_SYSTEM_SPLITBUTTON: return UIA_SplitButtonControlTypeId; + case ROLE_SYSTEM_SOUND: + case ROLE_SYSTEM_CURSOR: + case ROLE_SYSTEM_CARET: + case ROLE_SYSTEM_ALERT: + case ROLE_SYSTEM_CLIENT: + case ROLE_SYSTEM_CHART: + case ROLE_SYSTEM_DIALOG: + case ROLE_SYSTEM_BORDER: + case ROLE_SYSTEM_COLUMN: + case ROLE_SYSTEM_ROW: + case ROLE_SYSTEM_HELPBALLOON: + case ROLE_SYSTEM_CHARACTER: + case ROLE_SYSTEM_PROPERTYPAGE: + case ROLE_SYSTEM_DROPLIST: + case ROLE_SYSTEM_DIAL: + case ROLE_SYSTEM_HOTKEYFIELD: + case ROLE_SYSTEM_DIAGRAM: + case ROLE_SYSTEM_ANIMATION: + case ROLE_SYSTEM_EQUATION: + case ROLE_SYSTEM_WHITESPACE: + case ROLE_SYSTEM_IPADDRESS: + case ROLE_SYSTEM_OUTLINEBUTTON: + WARN("No UIA control type mapping for MSAA role %ld\n", role); + break; + + default: + FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role); + break; + } + + return 0; +} + /* * UiaProviderFromIAccessible IRawElementProviderSimple interface. */ @@ -42,6 +121,7 @@ struct msaa_provider { IAccessible *acc; VARIANT cid; HWND hwnd; + LONG control_type; };
static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface) @@ -106,9 +186,14 @@ HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, PROPERTYID prop_id, VARIANT *ret_val) { + struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface); + HRESULT hr; + VARIANT v; + TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
VariantInit(ret_val); + VariantInit(&v); switch (prop_id) { case UIA_ProviderDescriptionPropertyId: @@ -116,6 +201,19 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, V_BSTR(ret_val) = SysAllocString(L"Wine: MSAA Proxy"); break;
+ case UIA_ControlTypePropertyId: + if (!msaa_prov->control_type) + { + hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v); + if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4)) + msaa_prov->control_type = msaa_role_to_uia_control_type(V_I4(&v)); + } + + if (msaa_prov->control_type) + variant_init_i4(ret_val, msaa_prov->control_type); + + break; + default: FIXME("Unimplemented propertyId %d\n", prop_id); break; @@ -192,7 +290,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD if (!hwnd) return E_FAIL;
- msaa_prov = heap_alloc(sizeof(*msaa_prov)); + msaa_prov = heap_alloc_zero(sizeof(*msaa_prov)); if (!msaa_prov) return E_OUTOFMEMORY;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 65 ++++++++++++++++++++-- dlls/uiautomationcore/uia_provider.c | 39 +++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index c735e7a0bd5..f3e4b89a120 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -59,6 +59,7 @@ DEFINE_EXPECT(winproc_GETOBJECT_CLIENT); DEFINE_EXPECT(Accessible_accNavigate); DEFINE_EXPECT(Accessible_get_accParent); DEFINE_EXPECT(Accessible_get_accRole); +DEFINE_EXPECT(Accessible_get_accState); DEFINE_EXPECT(Accessible_child_accNavigate); DEFINE_EXPECT(Accessible_child_get_accParent);
@@ -72,6 +73,14 @@ static BOOL check_variant_i4(VARIANT *v, int val) return FALSE; }
+static BOOL check_variant_bool(VARIANT *v, BOOL val) +{ + if (V_VT(v) == VT_BOOL && V_BOOL(v) == (val ? VARIANT_TRUE : VARIANT_FALSE)) + return TRUE; + + return FALSE; +} + static struct Accessible { IAccessible IAccessible_iface; @@ -82,6 +91,7 @@ static struct Accessible HWND acc_hwnd; HWND ow_hwnd; INT role; + INT state; } Accessible, Accessible_child;
static inline struct Accessible* impl_from_Accessible(IAccessible *iface) @@ -217,7 +227,18 @@ static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_i static HRESULT WINAPI Accessible_get_accState(IAccessible *iface, VARIANT child_id, VARIANT *out_state) { - ok(0, "unexpected call\n"); + struct Accessible *This = impl_from_Accessible(iface); + + ok(This == &Accessible, "unexpected call\n"); + CHECK_EXPECT(Accessible_get_accState); + + if (This->state) + { + V_VT(out_state) = VT_I4; + V_I4(out_state) = This->state; + return S_OK; + } + return E_NOTIMPL; }
@@ -409,7 +430,7 @@ static struct Accessible Accessible = 1, NULL, 0, 0, - 0, + 0, 0, }; static struct Accessible Accessible_child = { @@ -418,7 +439,7 @@ static struct Accessible Accessible_child = 1, &Accessible.IAccessible_iface, 0, 0, - 0, + 0, 0, };
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -749,12 +770,24 @@ static const struct msaa_role_uia_type msaa_role_uia_types[] = { { ROLE_SYSTEM_OUTLINEBUTTON, 0 }, };
+struct msaa_state_uia_prop { + INT acc_state; + INT prop_id; +}; + +static const struct msaa_state_uia_prop msaa_state_uia_props[] = { + { STATE_SYSTEM_FOCUSED, UIA_HasKeyboardFocusPropertyId }, + { STATE_SYSTEM_FOCUSABLE, UIA_IsKeyboardFocusablePropertyId }, + { ~STATE_SYSTEM_UNAVAILABLE, UIA_IsEnabledPropertyId }, + { STATE_SYSTEM_PROTECTED, UIA_IsPasswordPropertyId }, +}; + static void test_uia_prov_from_acc_properties(void) { IRawElementProviderSimple *elprov; HRESULT hr; VARIANT v; - int i; + int i, x;
/* MSAA role to UIA control type test. */ for (i = 0; i < ARRAY_SIZE(msaa_role_uia_types); i++) @@ -822,6 +855,30 @@ static void test_uia_prov_from_acc_properties(void) Accessible.role = 0; IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + 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); + + /* UIA PropertyId's that correspond directly to individual MSAA state flags. */ + for (i = 0; i < ARRAY_SIZE(msaa_state_uia_props); i++) + { + const struct msaa_state_uia_prop *state = &msaa_state_uia_props[i]; + + for (x = 0; x < 2; x++) + { + Accessible.state = x ? state->acc_state : ~state->acc_state; + SET_EXPECT(Accessible_get_accState); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, state->prop_id, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(check_variant_bool(&v, x), "V_BOOL(&v) = %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + } + } + Accessible.state = 0; + + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); }
static void test_UiaProviderFromIAccessible(void) diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index ab2b8305b10..69bc78e34c0 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -32,6 +32,25 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static void variant_init_bool(VARIANT *v, BOOL val) +{ + V_VT(v) = VT_BOOL; + V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE; +} + +static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, LONG flag) +{ + HRESULT hr; + VARIANT v; + + VariantInit(&v); + hr = IAccessible_get_accState(acc, cid, &v); + if (SUCCEEDED(hr) && V_VT(&v) == VT_I4 && (V_I4(&v) & flag)) + return TRUE; + + return FALSE; +} + static LONG msaa_role_to_uia_control_type(LONG role) { switch (role) @@ -214,6 +233,26 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
break;
+ case UIA_HasKeyboardFocusPropertyId: + variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, + STATE_SYSTEM_FOCUSED)); + break; + + case UIA_IsKeyboardFocusablePropertyId: + variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, + STATE_SYSTEM_FOCUSABLE)); + break; + + case UIA_IsEnabledPropertyId: + variant_init_bool(ret_val, !msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, + STATE_SYSTEM_UNAVAILABLE)); + break; + + case UIA_IsPasswordPropertyId: + variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, + STATE_SYSTEM_PROTECTED)); + break; + default: FIXME("Unimplemented propertyId %d\n", prop_id); break;
On Wed, May 25, 2022 at 02:37:39PM -0400, Connor McAdams wrote:
static void test_UiaProviderFromIAccessible(void) diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index ab2b8305b10..69bc78e34c0 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -32,6 +32,25 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static void variant_init_bool(VARIANT *v, BOOL val) +{
- V_VT(v) = VT_BOOL;
- V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE;
+}
+static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, LONG flag)
DWORD/ULONG flag.
+{
- HRESULT hr;
- VARIANT v;
- VariantInit(&v);
- hr = IAccessible_get_accState(acc, cid, &v);
- if (SUCCEEDED(hr) && V_VT(&v) == VT_I4 && (V_I4(&v) & flag))
return TRUE;
- return FALSE;
+}
static LONG msaa_role_to_uia_control_type(LONG role) { switch (role) @@ -214,6 +233,26 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
break;
- case UIA_HasKeyboardFocusPropertyId:
variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
STATE_SYSTEM_FOCUSED));
Unless you have need for these two helpers down the line, I'd combine them, i.e. have _check_acc_state() return a VARIANT.
Huw.
P.S. bonus points will be awarded for using gitlab to send v2 ;-)
On Wed, Jun 08, 2022 at 10:04:43AM +0100, Huw Davies wrote:
On Wed, May 25, 2022 at 02:37:39PM -0400, Connor McAdams wrote:
static void test_UiaProviderFromIAccessible(void) diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index ab2b8305b10..69bc78e34c0 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -32,6 +32,25 @@ static void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static void variant_init_bool(VARIANT *v, BOOL val) +{
- V_VT(v) = VT_BOOL;
- V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE;
+}
+static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, LONG flag)
DWORD/ULONG flag.
+{
- HRESULT hr;
- VARIANT v;
- VariantInit(&v);
- hr = IAccessible_get_accState(acc, cid, &v);
- if (SUCCEEDED(hr) && V_VT(&v) == VT_I4 && (V_I4(&v) & flag))
return TRUE;
- return FALSE;
+}
static LONG msaa_role_to_uia_control_type(LONG role) { switch (role) @@ -214,6 +233,26 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
break;
- case UIA_HasKeyboardFocusPropertyId:
variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
STATE_SYSTEM_FOCUSED));
Unless you have need for these two helpers down the line, I'd combine them, i.e. have _check_acc_state() return a VARIANT.
I use msaa_check_acc_state separately in a later patch, so it helps to have them separate.
Huw.
P.S. bonus points will be awarded for using gitlab to send v2 ;-)
Okee doke, will fix the LONG flag and try out a gitlab PR.
Thanks!