From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 112 +++++++++++++++++++-- dlls/uiautomationcore/uia_client.c | 14 ++- dlls/uiautomationcore/uia_private.h | 2 + dlls/uiautomationcore/uia_provider.c | 60 +++++++---- 4 files changed, 160 insertions(+), 28 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ed0133d22b7..3ec275a874c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -90,6 +90,7 @@ DEFINE_EXPECT(Accessible2_get_accName); DEFINE_EXPECT(Accessible2_get_accRole); DEFINE_EXPECT(Accessible2_get_accState); DEFINE_EXPECT(Accessible2_accLocation); +DEFINE_EXPECT(Accessible_QI_IAccIdentity); DEFINE_EXPECT(Accessible2_QI_IAccIdentity); DEFINE_EXPECT(Accessible2_get_uniqueID); DEFINE_EXPECT(Accessible_child_accNavigate); @@ -177,7 +178,10 @@ static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid, { if (This == &Accessible2) CHECK_EXPECT(Accessible2_QI_IAccIdentity); - ok(This == &Accessible2, "unexpected call\n"); + else if (This == &Accessible) + CHECK_EXPECT(Accessible_QI_IAccIdentity); + + ok(This == &Accessible2 || This == &Accessible, "unexpected call\n"); return E_NOINTERFACE; }
@@ -6738,10 +6742,17 @@ static void test_UiaNodeFromHandle(const char *name) /* Only sent twice on Win7. */ SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); hr = UiaNodeFromHandle(hwnd, &node); - /* Windows 10 and below return E_FAIL, Windows 11 returns S_OK. */ - ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); + /* + * On all versions of Windows prior to Windows 11, this would fail due to + * COM being uninitialized, presumably because it tries to get an MSAA + * proxy. Windows 11 now has the thread end up in an implicit MTA after + * the call to UiaNodeFromHandle, which is probably why this now succeeds. + * I don't know of anything that relies on this behavior, so for now we + * won't match it. + */ + todo_wine ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); CHECK_CALLED(winproc_GETOBJECT_UiaRoot); - todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); + CHECK_CALLED(winproc_GETOBJECT_CLIENT); if (SUCCEEDED(hr)) UiaNodeRelease(node);
@@ -6757,7 +6768,7 @@ static void test_UiaNodeFromHandle(const char *name) hr = UiaNodeFromHandle(hwnd, &node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(winproc_GETOBJECT_UiaRoot); - todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); + CHECK_CALLED(winproc_GETOBJECT_CLIENT);
hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); ok(hr == S_OK, "Unexpected hr %#lx\n", hr); @@ -6783,7 +6794,7 @@ static void test_UiaNodeFromHandle(const char *name) hr = UiaNodeFromHandle(hwnd, &node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); CHECK_CALLED(winproc_GETOBJECT_UiaRoot); - todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); + CHECK_CALLED(winproc_GETOBJECT_CLIENT);
hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); ok(hr == S_OK, "Unexpected hr %#lx\n", hr); @@ -6818,7 +6829,7 @@ static void test_UiaNodeFromHandle(const char *name) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 1 || broken(Provider.ref == 2), "Unexpected refcnt %ld\n", Provider.ref); CHECK_CALLED(winproc_GETOBJECT_UiaRoot); - todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); + CHECK_CALLED(winproc_GETOBJECT_CLIENT);
hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); ok(hr == S_OK, "Unexpected hr %#lx\n", hr); @@ -12684,6 +12695,93 @@ static void test_default_clientside_providers(void) UiaNodeRelease(node); prov_root = NULL;
+ /* + * Test default ProviderType_Proxy clientside provider. Provider will be + * the HWND provider for this node, and Accessible will be the main + * provider. + */ + initialize_provider(&Provider, ProviderOptions_ClientSideProvider, hwnd, FALSE); + set_accessible_props(&Accessible, ROLE_SYSTEM_TEXT, STATE_SYSTEM_FOCUSABLE, 0, L"Accessible", 0, 0, 20, 20); + set_accessible_ia2_props(&Accessible, FALSE, 0); + acc_client = &Accessible.IAccessible_iface; + prov_root = child_win_prov_root = NULL; + + SET_EXPECT(Accessible_QI_IAccIdentity); + SET_EXPECT(Accessible_get_accParent); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent twice on Win7. */ + SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); + hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + ok(Accessible.ref >= 2, "Unexpected refcnt %ld\n", Accessible.ref); + todo_wine CHECK_CALLED(Accessible_QI_IAccIdentity); + todo_wine CHECK_CALLED(Accessible_get_accParent); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_CLIENT); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider", FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Main", NULL, TRUE); + VariantClear(&v); + + SET_EXPECT(Accessible_get_accRole); + hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == UIA_EditControlTypeId, "Unexpected I4 %#lx\n", V_I4(&v)); + VariantClear(&v); + CHECK_CALLED(Accessible_get_accRole); + + UiaNodeRelease(node); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + /* + * Unlike UiaProviderFromIAccessible which won't create a provider for an + * MSAA proxy, the default clientside proxy provider will. + */ + hwnd2 = CreateWindowA("EDIT", "", WS_VISIBLE | WS_CHILD | ES_PASSWORD, + 0, 0, 100, 100, hwnd, NULL, NULL, NULL); + initialize_provider(&Provider, ProviderOptions_ClientSideProvider, hwnd2, FALSE); + + /* Tries to get override provider from parent HWND. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd2); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider", FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Main", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Annotation", NULL, TRUE); + VariantClear(&v); + + hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == UIA_EditControlTypeId, "Unexpected I4 %#lx\n", V_I4(&v)); + VariantClear(&v); + + hr = UiaGetPropertyValue(node, UIA_IsPasswordPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_BOOL, "Unexpected VT %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "V_BOOL(&v) = %#x\n", V_BOOL(&v)); + VariantClear(&v); + + UiaNodeRelease(node); + DestroyWindow(hwnd2); + + set_accessible_props(&Accessible, 0, 0, 0, NULL, 0, 0, 0, 0); + acc_client = NULL; + prov_root = child_win_prov_root = NULL; + method_sequences_enabled = TRUE; DestroyWindow(hwnd); DestroyWindow(hwnd_child); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 4c1f0e3e480..e3eb5cd0561 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2653,8 +2653,20 @@ static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderT switch (prov_type) { case ProviderType_Proxy: - FIXME("Default ProviderType_Proxy MSAA provider unimplemented.\n"); + { + IAccessible *acc; + + hr = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc); + if (FAILED(hr) || !acc) + break; + + hr = create_msaa_provider(acc, CHILDID_SELF, hwnd, TRUE, &elprov); + if (FAILED(hr)) + WARN("Failed to create MSAA proxy provider with hr %#lx\n", hr); + + IAccessible_Release(acc); break; + }
case ProviderType_NonClientArea: FIXME("Default ProviderType_NonClientArea provider unimplemented.\n"); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 3b1f21645fb..8bc0436697d 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -155,3 +155,5 @@ HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; +HRESULT create_msaa_provider(IAccessible *acc, long child_id, HWND hwnd, BOOL known_root_acc, + IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 05fa40c25d0..bfb8c034f0c 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -796,7 +796,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, else acc = msaa_prov->acc;
- hr = UiaProviderFromIAccessible(acc, CHILDID_SELF, 0, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -827,7 +827,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -877,7 +877,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -1134,13 +1134,48 @@ static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl = { msaa_acc_provider_get_DefaultAction, };
+HRESULT create_msaa_provider(IAccessible *acc, long child_id, HWND hwnd, BOOL known_root_acc, + IRawElementProviderSimple **elprov) +{ + struct msaa_provider *msaa_prov = heap_alloc_zero(sizeof(*msaa_prov)); + + if (!msaa_prov) + return E_OUTOFMEMORY; + + msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl; + msaa_prov->IRawElementProviderFragment_iface.lpVtbl = &msaa_fragment_vtbl; + msaa_prov->ILegacyIAccessibleProvider_iface.lpVtbl = &msaa_acc_provider_vtbl; + msaa_prov->refcount = 1; + variant_init_i4(&msaa_prov->cid, child_id); + msaa_prov->acc = acc; + IAccessible_AddRef(acc); + msaa_prov->ia2 = msaa_acc_get_ia2(acc); + + if (!hwnd) + { + HRESULT hr; + + hr = WindowFromAccessibleObject(acc, &msaa_prov->hwnd); + if (FAILED(hr)) + WARN("WindowFromAccessibleObject failed with hr %#lx\n", hr); + } + else + msaa_prov->hwnd = hwnd; + + if (known_root_acc) + msaa_prov->root_acc_check_ran = msaa_prov->is_root_acc = TRUE; + + *elprov = &msaa_prov->IRawElementProviderSimple_iface; + + return S_OK; +} + /*********************************************************************** * UiaProviderFromIAccessible (uiautomationcore.@) */ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD flags, IRawElementProviderSimple **elprov) { - struct msaa_provider *msaa_prov; IServiceProvider *serv_prov; HWND hwnd = NULL; HRESULT hr; @@ -1184,22 +1219,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD if (!hwnd) return E_FAIL;
- msaa_prov = heap_alloc_zero(sizeof(*msaa_prov)); - if (!msaa_prov) - return E_OUTOFMEMORY; - - msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl; - msaa_prov->IRawElementProviderFragment_iface.lpVtbl = &msaa_fragment_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); - msaa_prov->acc = acc; - IAccessible_AddRef(acc); - msaa_prov->ia2 = msaa_acc_get_ia2(acc); - *elprov = &msaa_prov->IRawElementProviderSimple_iface; - - return S_OK; + return create_msaa_provider(acc, child_id, hwnd, FALSE, elprov); }
static HRESULT uia_get_hr_for_last_error(void)