From 9b1243fb6b7ef7a738dea1695b75f9f4d978c70b Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Thu, 23 Mar 2023 11:05:48 -0400 Subject: [PATCH] uiautomationcore: Implement UIA_BoundingRectanglePropertyId for default HWND provider. Signed-off-by: Connor McAdams --- dlls/uiautomationcore/tests/uiautomation.c | 126 +++++++++++++++++++++++++---- dlls/uiautomationcore/uia_provider.c | 109 +++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 16 deletions(-) diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6e84124d1b5..fafac811a04 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10211,6 +10211,19 @@ static HWND create_test_hwnd(const char *class_name) 0, 0, 100, 100, NULL, NULL, NULL, NULL); } +static HWND create_child_test_hwnd(const char *class_name, HWND parent) +{ + WNDCLASSA cls = { 0 }; + + cls.lpfnWndProc = child_test_wnd_proc; + cls.hInstance = GetModuleHandleA(NULL); + cls.lpszClassName = class_name; + RegisterClassA(&cls); + + return CreateWindowA(class_name, "Test child window", WS_CHILD, + 0, 0, 50, 50, parent, NULL, NULL, NULL); +} + static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd, BOOL block_hwnd_provs) { IUIAutomationElement *element; @@ -12397,18 +12410,67 @@ static const struct uia_hwnd_control_type_test hwnd_control_type_test[] = { { WS_CHILD | WS_CAPTION, WS_EX_TOOLWINDOW, UIA_WindowControlTypeId, PARENT_HWND_DESKTOP }, }; +static void create_base_hwnd_test_node(HWND hwnd, BOOL child_hwnd, struct Provider *main, struct Provider *nc, + HUIANODE *ret_node) +{ + ULONG main_ref, nc_ref; + HRESULT hr; + + initialize_provider(nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, hwnd, TRUE); + initialize_provider(main, ProviderOptions_ClientSideProvider, hwnd, TRUE); + nc->ignore_hwnd_prop = main->ignore_hwnd_prop = TRUE; + main_ref = main->ref; + nc_ref = nc->ref; + + if (!child_hwnd) + { + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + SET_EXPECT(winproc_GETOBJECT_CLIENT); + prov_root = &main->IRawElementProviderSimple_iface; + } + else + { + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + SET_EXPECT(winproc_GETOBJECT_CLIENT); + SET_EXPECT(child_winproc_GETOBJECT_UiaRoot); + child_win_prov_root = &main->IRawElementProviderSimple_iface; + } + + hr = UiaNodeFromProvider(&nc->IRawElementProviderSimple_iface, ret_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(main->ref == (main_ref + 1), "Unexpected refcnt %ld\n", main->ref); + ok(nc->ref == (nc_ref + 1), "Unexpected refcnt %ld\n", nc->ref); + if (child_hwnd) + { + /* Called while trying to get override provider. */ + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + } + else + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + Provider.ret_invalid_prop_type = Provider_nc.ret_invalid_prop_type = TRUE; +} + static void test_default_clientside_providers(void) { + struct UiaRect uia_rect = { 0 }; + HWND hwnd, hwnd_child; + RECT rect = { 0 }; + IUnknown *unk_ns; HUIANODE node; HRESULT hr; - HWND hwnd; VARIANT v; int i; CoInitializeEx(NULL, COINIT_MULTITHREADED); hwnd = create_test_hwnd("test_default_clientside_providers class"); + hwnd_child = create_child_test_hwnd("test_default_clientside_providers child class", hwnd); method_sequences_enabled = FALSE; + hr = UiaGetReservedNotSupportedValue(&unk_ns); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* * Test default BaseHwnd provider. Unlike the other default providers, the * default BaseHwnd IRawElementProviderSimple is not available to test @@ -12417,19 +12479,7 @@ static void test_default_clientside_providers(void) * providers will return nothing so that we can isolate properties coming * from the BaseHwnd provider. */ - initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, hwnd, TRUE); - initialize_provider(&Provider, ProviderOptions_ClientSideProvider, hwnd, TRUE); - Provider_nc.ignore_hwnd_prop = Provider.ignore_hwnd_prop = TRUE; - prov_root = &Provider.IRawElementProviderSimple_iface; - SET_EXPECT(winproc_GETOBJECT_UiaRoot); - /* Only sent on Win7. */ - SET_EXPECT(winproc_GETOBJECT_CLIENT); - hr = UiaNodeFromProvider(&Provider_nc.IRawElementProviderSimple_iface, &node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); - ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); - CHECK_CALLED(winproc_GETOBJECT_UiaRoot); - called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + create_base_hwnd_test_node(hwnd, FALSE, &Provider, &Provider_nc, &node); hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); ok(hr == S_OK, "Unexpected hr %#lx\n", hr); @@ -12443,17 +12493,58 @@ static void test_default_clientside_providers(void) Provider.ret_invalid_prop_type = Provider_nc.ret_invalid_prop_type = TRUE; test_node_hwnd_provider(node, hwnd); ok_method_sequence(default_hwnd_prov_props_seq, "default_hwnd_prov_props_seq"); + method_sequences_enabled = FALSE; + + /* Get the bounding rectangle from the default BaseHwnd provider. */ + GetWindowRect(hwnd, &rect); + set_uia_rect(&uia_rect, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top)); + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_rect_val(&v, &uia_rect); + VariantClear(&v); + + /* Minimized top-level HWNDs don't return a bounding rectangle. */ + ShowWindow(hwnd, SW_MINIMIZE); + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == unk_ns, "unexpected IUnknown %p\n", V_UNKNOWN(&v)); + VariantClear(&v); + + UiaNodeRelease(node); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider_nc.ref == 1, "Unexpected refcnt %ld\n", Provider_nc.ref); + + /* Create a child window node. */ + create_base_hwnd_test_node(hwnd_child, TRUE, &Provider, &Provider_nc, &node); + test_node_hwnd_provider(node, hwnd_child); + + /* Get the bounding rectangle from the default BaseHwnd provider. */ + GetWindowRect(hwnd_child, &rect); + set_uia_rect(&uia_rect, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top)); + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_rect_val(&v, &uia_rect); + VariantClear(&v); + + /* Minimized non top-level HWNDs return a bounding rectangle. */ + ShowWindow(hwnd_child, SW_MINIMIZE); + GetWindowRect(hwnd_child, &rect); + set_uia_rect(&uia_rect, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top)); + hr = UiaGetPropertyValue(node, UIA_BoundingRectanglePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_rect_val(&v, &uia_rect); + VariantClear(&v); UiaNodeRelease(node); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); ok(Provider_nc.ref == 1, "Unexpected refcnt %ld\n", Provider_nc.ref); - method_sequences_enabled = FALSE; VariantInit(&v); for (i = 0; i < ARRAY_SIZE(hwnd_control_type_test); i++) { const struct uia_hwnd_control_type_test *test = &hwnd_control_type_test[i]; - HWND hwnd2, parent; + HWND parent, hwnd2; if (test->parent_hwnd_type == PARENT_HWND_HWND) parent = hwnd; @@ -12498,8 +12589,11 @@ static void test_default_clientside_providers(void) method_sequences_enabled = TRUE; DestroyWindow(hwnd); + DestroyWindow(hwnd_child); UnregisterClassA("test_default_clientside_providers class", NULL); + UnregisterClassA("test_default_clientside_providers child class", NULL); + IUnknown_Release(unk_ns); CoUninitialize(); } diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 1609d53fe3c..db00676757a 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1273,6 +1273,7 @@ static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) */ struct base_hwnd_provider { IRawElementProviderSimple IRawElementProviderSimple_iface; + IRawElementProviderFragment IRawElementProviderFragment_iface; LONG refcount; HWND hwnd; @@ -1285,9 +1286,13 @@ static inline struct base_hwnd_provider *impl_from_base_hwnd_provider(IRawElemen static HRESULT WINAPI base_hwnd_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv) { + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface); + *ppv = NULL; if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown)) *ppv = iface; + else if (IsEqualIID(riid, &IID_IRawElementProviderFragment)) + *ppv = &base_hwnd_prov->IRawElementProviderFragment_iface; else return E_NOINTERFACE; @@ -1446,6 +1451,109 @@ static const IRawElementProviderSimpleVtbl base_hwnd_provider_vtbl = { base_hwnd_provider_get_HostRawElementProvider, }; +/* + * IRawElementProviderFragment interface for default ProviderType_BaseHwnd + * providers. + */ +static inline struct base_hwnd_provider *impl_from_base_hwnd_fragment(IRawElementProviderFragment *iface) +{ + return CONTAINING_RECORD(iface, struct base_hwnd_provider, IRawElementProviderFragment_iface); +} + +static HRESULT WINAPI base_hwnd_fragment_QueryInterface(IRawElementProviderFragment *iface, REFIID riid, + void **ppv) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface); + return IRawElementProviderSimple_QueryInterface(&base_hwnd_prov->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI base_hwnd_fragment_AddRef(IRawElementProviderFragment *iface) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface); + return IRawElementProviderSimple_AddRef(&base_hwnd_prov->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI base_hwnd_fragment_Release(IRawElementProviderFragment *iface) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface); + return IRawElementProviderSimple_Release(&base_hwnd_prov->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI base_hwnd_fragment_Navigate(IRawElementProviderFragment *iface, + enum NavigateDirection direction, IRawElementProviderFragment **ret_val) +{ + FIXME("%p, %d, %p: stub\n", iface, direction, ret_val); + *ret_val = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI base_hwnd_fragment_GetRuntimeId(IRawElementProviderFragment *iface, + SAFEARRAY **ret_val) +{ + FIXME("%p, %p: stub!\n", iface, ret_val); + *ret_val = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI base_hwnd_fragment_get_BoundingRectangle(IRawElementProviderFragment *iface, + struct UiaRect *ret_val) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface); + RECT rect = { 0 }; + + TRACE("%p, %p\n", iface, ret_val); + + memset(ret_val, 0, sizeof(*ret_val)); + + /* Top level minimized window - Return empty rect. */ + if (is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd)) + return S_OK; + + if (!GetWindowRect(base_hwnd_prov->hwnd, &rect)) + return uia_get_hr_for_last_error(); + + ret_val->left = rect.left; + ret_val->top = rect.top; + ret_val->width = (rect.right - rect.left); + ret_val->height = (rect.bottom - rect.top); + + return S_OK; +} + +static HRESULT WINAPI base_hwnd_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface, + SAFEARRAY **ret_val) +{ + FIXME("%p, %p: stub!\n", iface, ret_val); + *ret_val = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI base_hwnd_fragment_SetFocus(IRawElementProviderFragment *iface) +{ + FIXME("%p: stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI base_hwnd_fragment_get_FragmentRoot(IRawElementProviderFragment *iface, + IRawElementProviderFragmentRoot **ret_val) +{ + FIXME("%p, %p: stub!\n", iface, ret_val); + *ret_val = NULL; + return E_NOTIMPL; +} + +static const IRawElementProviderFragmentVtbl base_hwnd_fragment_vtbl = { + base_hwnd_fragment_QueryInterface, + base_hwnd_fragment_AddRef, + base_hwnd_fragment_Release, + base_hwnd_fragment_Navigate, + base_hwnd_fragment_GetRuntimeId, + base_hwnd_fragment_get_BoundingRectangle, + base_hwnd_fragment_GetEmbeddedFragmentRoots, + base_hwnd_fragment_SetFocus, + base_hwnd_fragment_get_FragmentRoot, +}; + HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) { struct base_hwnd_provider *base_hwnd_prov; @@ -1462,6 +1570,7 @@ HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) return E_OUTOFMEMORY; base_hwnd_prov->IRawElementProviderSimple_iface.lpVtbl = &base_hwnd_provider_vtbl; + base_hwnd_prov->IRawElementProviderFragment_iface.lpVtbl = &base_hwnd_fragment_vtbl; base_hwnd_prov->refcount = 1; base_hwnd_prov->hwnd = hwnd; *elprov = &base_hwnd_prov->IRawElementProviderSimple_iface; -- 2.11.4.GIT