From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 86 ++++++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 51 +++++++++++++ 2 files changed, 137 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ddeb3f3f055..6e84124d1b5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12361,12 +12361,49 @@ static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, winetest_pop_context(); }
+enum { + PARENT_HWND_NULL, + PARENT_HWND_HWND, + PARENT_HWND_DESKTOP, +}; + +struct uia_hwnd_control_type_test { + DWORD style; + DWORD style_ex; + int exp_control_type; + int parent_hwnd_type; +}; + +static const struct uia_hwnd_control_type_test hwnd_control_type_test[] = { + { WS_OVERLAPPEDWINDOW, 0, UIA_WindowControlTypeId, PARENT_HWND_NULL }, + /* Top-level window (parent is desktop window) is always a window control. */ + { WS_CHILD, 0, UIA_WindowControlTypeId, PARENT_HWND_DESKTOP }, + /* Not a top-level window, considered a pane. */ + { WS_CHILD, 0, UIA_PaneControlTypeId, PARENT_HWND_HWND }, + /* Not a top-level window, but WS_EX_APPWINDOW is always considered a window. */ + { WS_CHILD, WS_EX_APPWINDOW, UIA_WindowControlTypeId, PARENT_HWND_HWND }, + /* + * WS_POPUP is always a pane regardless of being a top level window, + * unless WS_CAPTION is set. + */ + { WS_CAPTION | WS_POPUP, 0, UIA_WindowControlTypeId, PARENT_HWND_DESKTOP }, + { WS_BORDER | WS_POPUP, 0, UIA_PaneControlTypeId, PARENT_HWND_DESKTOP }, + { WS_POPUP, 0, UIA_PaneControlTypeId, PARENT_HWND_DESKTOP }, + /* + * Top level window with WS_EX_TOOLWINDOW and without WS_CAPTION is + * considered a pane. + */ + { WS_CHILD, WS_EX_TOOLWINDOW, UIA_PaneControlTypeId, PARENT_HWND_DESKTOP }, + { WS_CHILD | WS_CAPTION, WS_EX_TOOLWINDOW, UIA_WindowControlTypeId, PARENT_HWND_DESKTOP }, +}; + static void test_default_clientside_providers(void) { HUIANODE node; HRESULT hr; HWND hwnd; VARIANT v; + int i;
CoInitializeEx(NULL, COINIT_MULTITHREADED); hwnd = create_test_hwnd("test_default_clientside_providers class"); @@ -12411,6 +12448,55 @@ static void test_default_clientside_providers(void) 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; + + if (test->parent_hwnd_type == PARENT_HWND_HWND) + parent = hwnd; + else if (test->parent_hwnd_type == PARENT_HWND_DESKTOP) + parent = GetDesktopWindow(); + else + parent = NULL; + + hwnd2 = CreateWindowExA(test->style_ex, "test_default_clientside_providers class", "Test window", test->style, + 0, 0, 100, 100, parent, NULL, NULL, NULL); + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, hwnd2, TRUE); + initialize_provider(&Provider, ProviderOptions_ClientSideProvider, hwnd2, TRUE); + Provider_nc.ignore_hwnd_prop = Provider.ignore_hwnd_prop = TRUE; + Provider.ret_invalid_prop_type = Provider_nc.ret_invalid_prop_type = TRUE; + + /* If parent is hwnd, it will be queried for an override provider. */ + if (test->style == WS_CHILD && (parent == hwnd)) + SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 2); + else + 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); + if (hwnd_control_type_test[i].style == WS_CHILD && (parent == hwnd)) + todo_wine CHECK_CALLED_MULTI(winproc_GETOBJECT_UiaRoot, 2); + else + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + 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) == test->exp_control_type, "Unexpected control type %ld\n", V_I4(&v)); + VariantClear(&v); + + UiaNodeRelease(node); + DestroyWindow(hwnd2); + } + + method_sequences_enabled = TRUE; DestroyWindow(hwnd); UnregisterClassA("test_default_clientside_providers class", NULL);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index ed00abaf33b..f0e971e1d6b 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1229,6 +1229,44 @@ static HRESULT uia_send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPAR return S_OK; }
+static BOOL is_top_level_hwnd(HWND hwnd) +{ + return (GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow()) ? TRUE : FALSE; +} + +static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) +{ + LONG_PTR style, ex_style; + + *control_type = 0; + if ((ex_style = GetWindowLongPtrW(hwnd, GWL_EXSTYLE)) & WS_EX_APPWINDOW) + { + *control_type = UIA_WindowControlTypeId; + return S_OK; + } + + if (!(style = GetWindowLongPtrW(hwnd, GWL_STYLE))) + return uia_get_hr_for_last_error(); + + /* + * Non-caption HWNDs that are popups or tool windows aren't considered full + * windows, only panes. + */ + if (((style & WS_CAPTION) != WS_CAPTION) && ((ex_style & WS_EX_TOOLWINDOW) || (style & WS_POPUP))) + { + *control_type = UIA_PaneControlTypeId; + return S_OK; + } + + /* Non top-level HWNDs are considered panes as well. */ + if (!is_top_level_hwnd(hwnd)) + *control_type = UIA_PaneControlTypeId; + else + *control_type = UIA_WindowControlTypeId; + + return S_OK; +} + /* * Default ProviderType_BaseHwnd IRawElementProviderSimple interface. */ @@ -1366,6 +1404,19 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim break; }
+ case UIA_ControlTypePropertyId: + { + int control_type; + + hr = get_uia_control_type_for_hwnd(base_hwnd_prov->hwnd, &control_type); + if (SUCCEEDED(hr)) + { + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = control_type; + } + break; + } + default: break; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- 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 f0e971e1d6b..b2af420dbeb 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1272,6 +1272,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; @@ -1284,9 +1285,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;
@@ -1445,6 +1450,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; @@ -1461,6 +1569,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;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 155 +++++++++++++++++---- dlls/uiautomationcore/uia_provider.c | 48 ++++++- 2 files changed, 172 insertions(+), 31 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index fafac811a04..ed0133d22b7 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6353,15 +6353,12 @@ static void test_UiaNodeFromHandle_client_proc(void)
memset(buf, 0, sizeof(buf)); GetWindowThreadProcessId(hwnd, &pid); - todo_wine ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); - if (lstrlenW(buf)) - { - check_node_provider_desc_prefix(buf, pid, hwnd); - check_node_provider_desc(buf, L"Main", L"Provider", TRUE); - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); - check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); - check_node_provider_desc_todo(V_BSTR(&v), L"Hwnd", NULL, TRUE); - } + ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); + check_node_provider_desc_prefix(buf, pid, hwnd); + check_node_provider_desc(buf, L"Main", L"Provider", TRUE); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Hwnd", NULL, TRUE);
VariantClear(&v);
@@ -6427,15 +6424,12 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
memset(buf, 0, sizeof(buf)); - todo_wine ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); - if (lstrlenW(buf)) - { - check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); - check_node_provider_desc(buf, L"Main", L"Provider", TRUE); - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); - check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); - check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); - } + ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); + check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); + check_node_provider_desc(buf, L"Main", L"Provider", TRUE); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE);
VariantClear(&v);
@@ -6583,15 +6577,12 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
memset(buf, 0, sizeof(buf)); - todo_wine ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); - if (lstrlenW(buf)) - { - check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); - check_node_provider_desc(buf, L"Main", L"Provider", TRUE); - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); - check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); - check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); - } + ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); + check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); + check_node_provider_desc(buf, L"Main", L"Provider", TRUE); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); VariantClear(&v);
Provider.ignore_hwnd_prop = FALSE; @@ -12452,10 +12443,60 @@ static void create_base_hwnd_test_node(HWND hwnd, BOOL child_hwnd, struct Provid Provider.ret_invalid_prop_type = Provider_nc.ret_invalid_prop_type = TRUE; }
+#define test_node_hwnd_provider_navigation( node, dir, exp_dest_hwnd ) \ + test_node_hwnd_provider_navigation_( (node), (dir), (exp_dest_hwnd), __FILE__, __LINE__) +static void test_node_hwnd_provider_navigation_(HUIANODE node, int nav_dir, HWND exp_dest_hwnd, const char *file, + int line) +{ + struct UiaCacheRequest cache_req = { NULL, TreeScope_Element, NULL, 0, NULL, 0, AutomationElementMode_Full }; + const WCHAR *exp_tree_struct = exp_dest_hwnd ? L"P)" : L""; + SAFEARRAY *out_req = NULL; + BSTR tree_struct = NULL; + LONG idx[2] = { 0 }; + HUIANODE tmp_node; + HRESULT hr; + VARIANT v; + int i; + + hr = UiaNavigate(node, nav_dir, (struct UiaCondition *)&UiaTrueCondition, &cache_req, &out_req, &tree_struct); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok_(file, line)(!!out_req, "out_req == NULL\n"); + ok_(file, line)(!!tree_struct, "tree_struct == NULL\n"); + if (!exp_dest_hwnd) + goto exit; + + for (i = 0; i < 2; i++) + { + hr = SafeArrayGetLBound(out_req, 1 + i, &idx[i]); + ok_(file, line)(hr == S_OK, "SafeArrayGetLBound unexpected hr %#lx\n", hr); + } + + hr = SafeArrayGetElement(out_req, idx, &v); + ok_(file, line)(hr == S_OK, "SafeArrayGetElement unexpected hr %#lx\n", hr); + + hr = UiaHUiaNodeFromVariant(&v, &tmp_node); + ok_(file, line)(hr == S_OK, "UiaHUiaNodeFromVariant unexpected hr %#lx\n", hr); + ok_(file, line)(!!tmp_node, "tmp_node == NULL\n"); + VariantClear(&v); + + hr = UiaGetPropertyValue(tmp_node, UIA_NativeWindowHandlePropertyId, &v); + ok_(file, line)(hr == S_OK, "UiaGetPropertyValue unexpected hr %#lx\n", hr); + ok_(file, line)(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v)); + ok_(file, line)(V_I4(&v) == HandleToUlong(exp_dest_hwnd), "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), + HandleToUlong(exp_dest_hwnd)); + VariantClear(&v); + UiaNodeRelease(tmp_node); + +exit: + ok_(file, line)(!wcscmp(tree_struct, exp_tree_struct), "unexpected tree structure %s\n", debugstr_w(tree_struct)); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); +} + static void test_default_clientside_providers(void) { struct UiaRect uia_rect = { 0 }; - HWND hwnd, hwnd_child; + HWND hwnd, hwnd_child, hwnd2; RECT rect = { 0 }; IUnknown *unk_ns; HUIANODE node; @@ -12544,7 +12585,7 @@ static void test_default_clientside_providers(void) 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 parent, hwnd2; + HWND parent;
if (test->parent_hwnd_type == PARENT_HWND_HWND) parent = hwnd; @@ -12587,6 +12628,62 @@ static void test_default_clientside_providers(void) DestroyWindow(hwnd2); }
+ /* + * Default ProviderType_BaseHwnd provider navigation tests. + */ + create_base_hwnd_test_node(hwnd, FALSE, &Provider, &Provider_nc, &node); + test_node_hwnd_provider(node, hwnd); + + /* + * Navigate to the parent of our top-level HWND, will get a node + * representing the desktop HWND. + */ + test_node_hwnd_provider_navigation(node, NavigateDirection_Parent, GetDesktopWindow()); + UiaNodeRelease(node); + + /* + * Create a node repesenting an HWND that is a top-level window, but is + * owned by another window. For top-level HWNDs, parent navigation will go + * to the owner instead of the parent. + */ + hwnd2 = CreateWindowA("test_default_clientside_providers class", "Test window", WS_POPUP, 0, 0, 50, 50, hwnd, NULL, + NULL, NULL); + ok(GetAncestor(hwnd2, GA_PARENT) == GetDesktopWindow(), "unexpected parent hwnd"); + ok(GetWindow(hwnd2, GW_OWNER) == hwnd, "unexpected owner hwnd"); + create_base_hwnd_test_node(hwnd2, FALSE, &Provider, &Provider_nc, &node); + test_node_hwnd_provider(node, hwnd2); + + /* Navigate to the parent. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + test_node_hwnd_provider_navigation(node, NavigateDirection_Parent, hwnd); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + UiaNodeRelease(node); + DestroyWindow(hwnd2); + + /* + * Create a node for our child window. + */ + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, hwnd, TRUE); + Provider2.ignore_hwnd_prop = TRUE; + prov_root = &Provider2.IRawElementProviderSimple_iface; + create_base_hwnd_test_node(hwnd_child, TRUE, &Provider, &Provider_nc, &node); + test_node_hwnd_provider(node, hwnd_child); + + /* Navigate to parent. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + test_node_hwnd_provider_navigation(node, NavigateDirection_Parent, hwnd); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + UiaNodeRelease(node); + prov_root = NULL; + method_sequences_enabled = TRUE; DestroyWindow(hwnd); DestroyWindow(hwnd_child); diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index b2af420dbeb..05fa40c25d0 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1481,9 +1481,53 @@ static ULONG WINAPI base_hwnd_fragment_Release(IRawElementProviderFragment *ifac 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); + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface); + IRawElementProviderSimple *elprov = NULL; + HRESULT hr = S_OK; + + TRACE("%p, %d, %p\n", iface, direction, ret_val); + *ret_val = NULL; - return E_NOTIMPL; + + switch (direction) + { + case NavigateDirection_Parent: + { + HWND parent, owner; + + /* + * Top level owned windows have their owner window as a parent instead + * of the desktop window. + */ + if (is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER))) + parent = owner; + else + parent = GetAncestor(base_hwnd_prov->hwnd, GA_PARENT); + + if (parent) + hr = create_base_hwnd_provider(parent, &elprov); + break; + } + + case NavigateDirection_FirstChild: + case NavigateDirection_LastChild: + case NavigateDirection_PreviousSibling: + case NavigateDirection_NextSibling: + FIXME("Unimplemented NavigateDirection %d\n", direction); + return E_NOTIMPL; + + default: + FIXME("Invalid NavigateDirection %d\n", direction); + return E_INVALIDARG; + } + + if (elprov) + { + hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)ret_val); + IRawElementProviderSimple_Release(elprov); + } + + return hr; }
static HRESULT WINAPI base_hwnd_fragment_GetRuntimeId(IRawElementProviderFragment *iface,
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)
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_provider.c:
+{
- return (GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow()) ? TRUE : FALSE;
+}
+static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) +{
- LONG_PTR style, ex_style;
- *control_type = 0;
- if ((ex_style = GetWindowLongPtrW(hwnd, GWL_EXSTYLE)) & WS_EX_APPWINDOW)
- {
*control_type = UIA_WindowControlTypeId;
return S_OK;
- }
- if (!(style = GetWindowLongPtrW(hwnd, GWL_STYLE)))
Is it not possible for a window to have a style of 0?
On Mon Apr 17 18:21:40 2023 +0000, Esme Povirk wrote:
Is it not possible for a window to have a style of 0?
I haven't run across a case where a valid window has a style of 0, but looking at the win32u code I guess it's possible that it can happen and the window is still valid. I should probably change this to: ``` if (!(style = GetWindowLongPtrW(hwnd, GWL_STYLE)) && (GetLastError() != NO_ERROR)) ```