From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 609 +++++++++++++++++++- dlls/uiautomationcore/uia_client.c | 9 + dlls/uiautomationcore/uiautomationcore.spec | 2 +- include/uiautomationcoreapi.h | 1 + 4 files changed, 604 insertions(+), 17 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 40f6cdd9f38..dc3ca279fb1 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1157,6 +1157,7 @@ static struct Provider struct UiaRect bounds_rect; struct Provider_value_pattern_data value_pattern_data; struct Provider_legacy_accessible_pattern_data legacy_acc_pattern_data; + IRawElementProviderFragment *focus_prov; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); @@ -1223,6 +1224,7 @@ enum { FRAG_GET_RUNTIME_ID, FRAG_GET_FRAGMENT_ROOT, FRAG_GET_BOUNDING_RECT, + FRAG_ROOT_GET_FOCUS, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, };
@@ -1235,6 +1237,7 @@ static const char *prov_method_str[] = { "GetRuntimeId", "get_FragmentRoot", "get_BoundingRectangle", + "GetFocus", "GetOverrideProviderForHwnd", };
@@ -2090,8 +2093,21 @@ static HRESULT WINAPI ProviderFragmentRoot_ElementProviderFromPoint(IRawElementP static HRESULT WINAPI ProviderFragmentRoot_GetFocus(IRawElementProviderFragmentRoot *iface, IRawElementProviderFragment **ret_val) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + struct Provider *Provider = impl_from_ProviderFragmentRoot(iface); + + add_method_call(Provider, FRAG_ROOT_GET_FOCUS); + if (Provider->expected_tid) + ok(Provider->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + Provider->last_call_tid = GetCurrentThreadId(); + + *ret_val = NULL; + if (Provider->focus_prov) + { + *ret_val = Provider->focus_prov; + IRawElementProviderFragment_AddRef(*ret_val); + } + + return S_OK; }
static const IRawElementProviderFragmentRootVtbl ProviderFragmentRootVtbl = { @@ -2514,6 +2530,8 @@ DEFINE_PROVIDER(child_child); DEFINE_PROVIDER(child_child2); DEFINE_PROVIDER(child2_child); DEFINE_PROVIDER(child2_child_child); +DEFINE_PROVIDER(hwnd2); +DEFINE_PROVIDER(nc2);
static IAccessible *acc_client; static IRawElementProviderSimple *prov_root; @@ -7233,7 +7251,10 @@ static SAFEARRAY WINAPI *test_uia_provider_callback(HWND hwnd, enum ProviderType { case ProviderType_BaseHwnd: CHECK_EXPECT(prov_callback_base_hwnd); - elprov = base_hwnd_prov; + if (hwnd == Provider_hwnd2.hwnd) + elprov = &Provider_hwnd2.IRawElementProviderSimple_iface; + else + elprov = base_hwnd_prov; break;
case ProviderType_Proxy: @@ -7251,7 +7272,10 @@ static SAFEARRAY WINAPI *test_uia_provider_callback(HWND hwnd, enum ProviderType
case ProviderType_NonClientArea: CHECK_EXPECT(prov_callback_nonclient); - elprov = nc_prov; + if (hwnd == Provider_nc2.hwnd) + elprov = &Provider_nc2.IRawElementProviderSimple_iface; + else + elprov = nc_prov; break;
default: @@ -9428,6 +9452,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, memset(&prov->bounds_rect, 0, sizeof(prov->bounds_rect)); memset(&prov->value_pattern_data, 0, sizeof(prov->value_pattern_data)); memset(&prov->legacy_acc_pattern_data, 0, sizeof(prov->legacy_acc_pattern_data)); + prov->focus_prov = NULL; if (initialize_nav_links) { prov->frag_root = NULL; @@ -12391,6 +12416,23 @@ static void test_CUIAutomation_TreeWalker_ifaces(IUIAutomation *uia_iface) UnregisterClassA("test_CUIAutomation_TreeWalker_ifaces class", NULL); }
+static void set_clientside_providers_for_hwnd(struct Provider *proxy_prov, struct Provider *nc_prov, + struct Provider *hwnd_prov, HWND hwnd) +{ + if (proxy_prov) + { + initialize_provider(proxy_prov, ProviderOptions_ClientSideProvider, hwnd, TRUE); + proxy_prov->frag_root = &proxy_prov->IRawElementProviderFragmentRoot_iface; + proxy_prov->ignore_hwnd_prop = TRUE; + } + + initialize_provider(hwnd_prov, ProviderOptions_ClientSideProvider, hwnd, TRUE); + initialize_provider(nc_prov, ProviderOptions_NonClientAreaProvider | ProviderOptions_ClientSideProvider, hwnd, TRUE); + hwnd_prov->frag_root = &hwnd_prov->IRawElementProviderFragmentRoot_iface; + nc_prov->frag_root = &nc_prov->IRawElementProviderFragmentRoot_iface; + nc_prov->ignore_hwnd_prop = TRUE; +} + static void test_GetRootElement(IUIAutomation *uia_iface) { IUIAutomationElement *element; @@ -12402,12 +12444,7 @@ static void test_GetRootElement(IUIAutomation *uia_iface)
UiaRegisterProviderCallback(test_uia_provider_callback);
- initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); - initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, - GetDesktopWindow(), TRUE); - initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); - Provider_proxy.ignore_hwnd_prop = TRUE; - + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; nc_prov = &Provider_nc.IRawElementProviderSimple_iface; @@ -13057,12 +13094,7 @@ static void test_UiaGetRootNode(void) * UiaGetRootNode is the same as calling UiaNodeFromHandle with the * desktop window handle. */ - initialize_provider(&Provider_hwnd, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); - initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, - GetDesktopWindow(), TRUE); - initialize_provider(&Provider_proxy, ProviderOptions_ClientSideProvider, GetDesktopWindow(), TRUE); - Provider_proxy.ignore_hwnd_prop = TRUE; - + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; nc_prov = &Provider_nc.IRawElementProviderSimple_iface; @@ -13105,6 +13137,550 @@ static void test_UiaGetRootNode(void) CoUninitialize(); }
+/* + * Sequence of method calls when creating a desktop node with + * test_uia_provider_callback set as the provider callback. + */ +#define DESKTOP_PROXY_NODE_SEQ \ + /* These two are only done on Win10v1809+. */ \ + { &Provider_hwnd, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, \ + { &Provider_hwnd, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { &Provider_proxy, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { &Provider_nc, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { &Provider_hwnd, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + /* These three only done on Win10+. */ \ + { &Provider_proxy, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ + { &Provider_hwnd, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + +static const struct prov_method_sequence node_from_focus_seq1[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd, FRAG_ROOT_GET_FOCUS }, + { &Provider_proxy, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_proxy, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_nc, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_hwnd, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq2[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc2, FRAG_ROOT_GET_FOCUS }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_nc2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq3[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, FRAG_ROOT_GET_FOCUS }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_nc2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq4[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_ROOT_GET_FOCUS }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_nc2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq5[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_ROOT_GET_FOCUS }, + NODE_CREATE_SEQ(&Provider2), + { &Provider2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider2, FRAG_GET_RUNTIME_ID }, + { &Provider2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq6[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_ROOT_GET_FOCUS }, + NODE_CREATE_SEQ(&Provider2), + { &Provider2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider2, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + /* Only done on Win10v1507 and below. */ + { &Provider2, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq7[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + { &Provider_nc, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd, FRAG_ROOT_GET_FOCUS }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + NODE_CREATE_SEQ2(&Provider), + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+ */ + { &Provider, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + /* These three only done on Win10+. */ \ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_ROOT_GET_FOCUS }, + NODE_CREATE_SEQ(&Provider_child_child), + { &Provider_child_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_IsControlElementPropertyId */ + { &Provider_child_child, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence node_from_focus_seq8[] = { + DESKTOP_PROXY_NODE_SEQ, + { &Provider_proxy, FRAG_ROOT_GET_FOCUS }, + NODE_CREATE_SEQ(&Provider2), + { &Provider2, PROV_GET_PROPERTY_VALUE }, /* UIA_IsControlElementPropertyId */ + { &Provider2, FRAG_GET_RUNTIME_ID }, + { &Provider2, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static void test_UiaNodeFromFocus(void) +{ + struct Provider_prop_override prop_override; + struct node_provider_desc exp_node_desc; + struct UiaPropertyCondition prop_cond; + LONG exp_lbound[2], exp_elems[2]; + struct UiaCacheRequest cache_req; + struct UiaNotCondition not_cond; + SAFEARRAY *out_req; + BSTR tree_struct; + int cache_prop; + HRESULT hr; + HWND hwnd; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + hwnd = create_test_hwnd("UiaNodeFromFocus class"); + + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* Set clientside providers for our test window and the desktop. */ + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + + set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + prov_root = &Provider.IRawElementProviderSimple_iface; + Provider.ignore_hwnd_prop = TRUE; + + /* + * Nodes are normalized against the cache request view condition. Here, + * we're setting it to the same as the default ControlView. + */ + variant_init_bool(&v, FALSE); + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + set_not_condition(¬_cond, (struct UiaCondition *)&prop_cond); + cache_prop = UIA_RuntimeIdPropertyId; + set_cache_request(&cache_req, (struct UiaCondition *)¬_cond, TreeScope_Element, &cache_prop, 1, NULL, 0, + AutomationElementMode_Full); + + /* + * None of the providers for the desktop node return a provider from + * IRawElementProviderFragmentRoot::GetFocus, so we just get the + * desktop node. + */ + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED(prov_callback_proxy); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), GetDesktopWindow()); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_proxy", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq1, "node_from_focus_seq1"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* Provider_hwnd returns Provider_hwnd2 from GetFocus. */ + Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq2, "node_from_focus_seq2"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* + * Provider_proxy returns Provider from GetFocus. The provider that + * creates the node will not have GetFocus called on it, presumably to + * avoid returning the same provider twice. + */ + Provider_hwnd.focus_prov = NULL; + Provider_proxy.focus_prov = &Provider.IRawElementProviderFragment_iface; + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq3, "node_from_focus_seq3"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* + * Provider_nc returns Provider_nc2 from GetFocus. Again, the provider + * that creates the node doesn't have GetFocus called on it. + */ + Provider_proxy.focus_prov = NULL; + Provider_nc.focus_prov = &Provider_nc2.IRawElementProviderFragment_iface; + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq4, "node_from_focus_seq4"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* + * Provider_hwnd returns Provider_hwnd2 from GetFocus, Provider returns + * Provider2. + */ + Provider_nc.focus_prov = NULL; + Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, NULL, TRUE); + Provider.focus_prov = &Provider2.IRawElementProviderFragment_iface; + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq5, "node_from_focus_seq5"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* + * Same test as before, except this time Provider2 doesn't match our cache + * request view condition. Since it has no ancestors, we will get an empty + * cache request. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider2, &prop_override, 1); + + Provider_nc.focus_prov = NULL; + Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; + Provider.focus_prov = &Provider2.IRawElementProviderFragment_iface; + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!out_req, "out_req != NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + if (tree_struct) + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + SysFreeString(tree_struct); + if (SUCCEEDED(hr)) + ok_method_sequence(node_from_focus_seq6, "node_from_focus_seq6"); + set_provider_prop_override(&Provider2, NULL, 0); + + /* + * Provider_hwnd returns Provider_hwnd2 from GetFocus, Provider returns + * Provider_child_child. Provider_child_child doesn't match our cache + * request view condition, but its parent Provider_child does. + */ + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + initialize_provider(&Provider_child_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + provider_add_child(&Provider, &Provider_child); + provider_add_child(&Provider_child, &Provider_child_child); + Provider.focus_prov = &Provider_child_child.IRawElementProviderFragment_iface; + Provider_nc.focus_prov = NULL; + Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; + + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider_child_child, &prop_override, 1); + + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq7, "node_from_focus_seq7"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + /* + * Provider_proxy returns Provider2, which has no HWND associated. + */ + Provider_hwnd.focus_prov = Provider.focus_prov = NULL; + Provider_proxy.focus_prov = &Provider2.IRawElementProviderFragment_iface; + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, NULL, TRUE); + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + out_req = NULL; + tree_struct = NULL; + hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!out_req, "out_req == NULL\n"); + todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED(prov_callback_proxy); + + if (SUCCEEDED(hr)) + { + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq8, "node_from_focus_seq8"); + + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + CoUninitialize(); + UiaRegisterProviderCallback(NULL); + DestroyWindow(hwnd); + UnregisterClassA("UiaNodeFromFocus class", NULL); +} + /* * Once a process returns a UI Automation provider with * UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This @@ -13174,6 +13750,7 @@ START_TEST(uiautomation) test_CUIAutomation(); test_default_clientside_providers(); test_UiaGetRootNode(); + test_UiaNodeFromFocus(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible"); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 64fd8b3b1cb..1329cc43db0 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2435,6 +2435,15 @@ HRESULT WINAPI UiaGetRootNode(HUIANODE *huianode) return UiaNodeFromHandle(GetDesktopWindow(), huianode); }
+/*********************************************************************** + * UiaNodeFromFocus (uiautomationcore.@) + */ +HRESULT WINAPI UiaNodeFromFocus(struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct) +{ + FIXME("(%p, %p, %p): stub\n", cache_req, out_req, tree_struct); + return E_NOTIMPL; +} + /*********************************************************************** * UiaNodeRelease (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec index 34856be6b36..686594debba 100644 --- a/dlls/uiautomationcore/uiautomationcore.spec +++ b/dlls/uiautomationcore/uiautomationcore.spec @@ -76,7 +76,7 @@ #@ stub UiaIAccessibleFromProvider @ stdcall UiaLookupId(long ptr) @ stdcall UiaNavigate(ptr long ptr ptr ptr ptr) -@ stub UiaNodeFromFocus +@ stdcall UiaNodeFromFocus(ptr ptr ptr) @ stdcall UiaNodeFromHandle(long ptr) @ stub UiaNodeFromPoint @ stdcall UiaNodeFromProvider(ptr ptr) diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h index 320bb814b9c..9d131abc6cc 100644 --- a/include/uiautomationcoreapi.h +++ b/include/uiautomationcoreapi.h @@ -545,6 +545,7 @@ HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id); HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode); HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode); HRESULT WINAPI UiaGetRootNode(HUIANODE *huianode); +HRESULT WINAPI UiaNodeFromFocus(struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct); HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov); HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state, struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 263 ++++++++++----------- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 150 +++++++++++- 3 files changed, 269 insertions(+), 145 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index dc3ca279fb1..b010711c2bb 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13394,28 +13394,25 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED(prov_callback_nonclient); - todo_wine CHECK_CALLED(prov_callback_proxy); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), GetDesktopWindow()); - add_provider_desc(&exp_node_desc, L"Main", L"Provider_proxy", TRUE); - add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc", FALSE); - add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd", FALSE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq1, "node_from_focus_seq1"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), GetDesktopWindow()); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_proxy", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq1, "node_from_focus_seq1");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* Provider_hwnd returns Provider_hwnd2 from GetFocus. */ Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; @@ -13426,29 +13423,26 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); - add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); - add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq2, "node_from_focus_seq2"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq2, "node_from_focus_seq2");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* * Provider_proxy returns Provider from GetFocus. The provider that @@ -13464,29 +13458,26 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); - todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); - add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); - add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq3, "node_from_focus_seq3"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq3, "node_from_focus_seq3");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* * Provider_nc returns Provider_nc2 from GetFocus. Again, the provider @@ -13501,29 +13492,26 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); - todo_wine CHECK_CALLED(prov_callback_nonclient); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); - add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); - add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); - add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq4, "node_from_focus_seq4"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq4, "node_from_focus_seq4");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* * Provider_hwnd returns Provider_hwnd2 from GetFocus, Provider returns @@ -13540,27 +13528,24 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); - add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq5, "node_from_focus_seq5"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq5, "node_from_focus_seq5");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* * Same test as before, except this time Provider2 doesn't match our cache @@ -13581,19 +13566,17 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!out_req, "out_req != NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - if (tree_struct) - ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + ok(!!tree_struct, "tree_struct == NULL\n"); + ok(!wcscmp(tree_struct, L""), "tree structure %s\n", debugstr_w(tree_struct)); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
SysFreeString(tree_struct); - if (SUCCEEDED(hr)) - ok_method_sequence(node_from_focus_seq6, "node_from_focus_seq6"); + ok_method_sequence(node_from_focus_seq6, "node_from_focus_seq6"); set_provider_prop_override(&Provider2, NULL, 0);
/* @@ -13620,27 +13603,24 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); - add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq7, "node_from_focus_seq7"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq7, "node_from_focus_seq7");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
/* * Provider_proxy returns Provider2, which has no HWND associated. @@ -13654,26 +13634,23 @@ static void test_UiaNodeFromFocus(void) out_req = NULL; tree_struct = NULL; hr = UiaNodeFromFocus(&cache_req, &out_req, &tree_struct); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!out_req, "out_req == NULL\n"); - todo_wine ok(!!tree_struct, "tree_struct == NULL\n"); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED(prov_callback_nonclient); - todo_wine CHECK_CALLED(prov_callback_proxy); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!out_req, "out_req == NULL\n"); + ok(!!tree_struct, "tree_struct == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy);
- if (SUCCEEDED(hr)) - { - init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); - add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); - exp_lbound[0] = exp_lbound[1] = 0; - exp_elems[0] = 1; - exp_elems[1] = 2; - test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); - ok_method_sequence(node_from_focus_seq8, "node_from_focus_seq8"); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = 1; + exp_elems[1] = 2; + test_cache_req_sa(out_req, exp_lbound, exp_elems, &exp_node_desc); + ok_method_sequence(node_from_focus_seq8, "node_from_focus_seq8");
- SafeArrayDestroy(out_req); - SysFreeString(tree_struct); - } + SafeArrayDestroy(out_req); + SysFreeString(tree_struct);
CoUninitialize(); UiaRegisterProviderCallback(NULL); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 7272a09bfd6..b727184ff37 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -66,6 +66,7 @@ library UIA_wine_private HRESULT get_prov_opts([out, retval]int *out_opts); HRESULT has_parent([out, retval]BOOL *out_val); HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val); + HRESULT get_focus([out, retval]VARIANT *ret_val); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1329cc43db0..02a33430f51 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -446,6 +446,22 @@ static HRESULT get_navigate_from_node_provider(IWineUiaNode *node, int idx, int return hr; }
+static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT *ret_val) +{ + IWineUiaProvider *prov; + HRESULT hr; + + VariantInit(ret_val); + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_get_focus(prov, ret_val); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1762,6 +1778,38 @@ static HRESULT WINAPI uia_provider_navigate(IWineUiaProvider *iface, int nav_dir return S_OK; }
+static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *out_val) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IRawElementProviderFragmentRoot *elroot; + IRawElementProviderFragment *elfrag; + IRawElementProviderSimple *elprov; + HRESULT hr; + + TRACE("%p, %p\n", iface, out_val); + + VariantInit(out_val); + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragmentRoot, (void **)&elroot); + if (FAILED(hr)) + return S_OK; + + hr = IRawElementProviderFragmentRoot_GetFocus(elroot, &elfrag); + IRawElementProviderFragmentRoot_Release(elroot); + if (FAILED(hr) || !elfrag) + return hr; + + hr = IRawElementProviderFragment_QueryInterface(elfrag, &IID_IRawElementProviderSimple, (void **)&elprov); + IRawElementProviderFragment_Release(elfrag); + if (SUCCEEDED(hr)) + { + hr = get_variant_for_elprov_node(elprov, prov->return_nested_node, out_val); + if (FAILED(hr)) + VariantClear(out_val); + } + + return hr; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1770,6 +1818,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_get_prov_opts, uia_provider_has_parent, uia_provider_navigate, + uia_provider_get_focus, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2170,6 +2219,30 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, return S_OK; }
+static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface, VARIANT *out_val) +{ + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + HUIANODE node; + HRESULT hr; + VARIANT v; + + TRACE("%p, %p\n", iface, out_val); + + VariantInit(out_val); + hr = get_focus_from_node_provider(prov->nested_node, 0, &v); + if (FAILED(hr) || V_VT(&v) == VT_EMPTY) + return hr; + + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + if (FAILED(hr)) + return hr; + + get_variant_for_node(node, out_val); + VariantClear(&v); + + return S_OK; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2178,6 +2251,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_get_prov_opts, uia_nested_node_provider_has_parent, uia_nested_node_provider_navigate, + uia_nested_node_provider_get_focus, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) @@ -2435,13 +2509,85 @@ HRESULT WINAPI UiaGetRootNode(HUIANODE *huianode) return UiaNodeFromHandle(GetDesktopWindow(), huianode); }
+static HRESULT get_focused_uia_node(HUIANODE in_node, HUIANODE *out_node) +{ + struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)in_node); + const BOOL desktop_node = (node->hwnd == GetDesktopWindow()); + HRESULT hr = S_OK; + VARIANT v; + int i; + + *out_node = NULL; + VariantInit(&v); + for (i = 0; i < node->prov_count; i++) + { + /* + * When getting focus from nodes other than the desktop, we ignore + * both the node's creator provider and its HWND provider. This avoids + * the problem of returning the same provider twice from GetFocus. + */ + if (!desktop_node && ((i == node->creator_prov_idx) || + (get_node_provider_type_at_idx(node, i) == PROV_TYPE_HWND))) + continue; + + hr = get_focus_from_node_provider(&node->IWineUiaNode_iface, i, &v); + if (FAILED(hr)) + break; + + if (V_VT(&v) != VT_EMPTY) + { + hr = UiaHUiaNodeFromVariant(&v, out_node); + if (FAILED(hr)) + out_node = NULL; + break; + } + } + + return hr; +} + /*********************************************************************** * UiaNodeFromFocus (uiautomationcore.@) */ HRESULT WINAPI UiaNodeFromFocus(struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct) { - FIXME("(%p, %p, %p): stub\n", cache_req, out_req, tree_struct); - return E_NOTIMPL; + HUIANODE node, node2; + HRESULT hr; + int i; + + TRACE("(%p, %p, %p)\n", cache_req, out_req, tree_struct); + + if (!cache_req || !out_req || !tree_struct) + return E_INVALIDARG; + + *out_req = NULL; + *tree_struct = NULL; + + hr = UiaGetRootNode(&node); + if (FAILED(hr)) + return hr; + + /* We only go two nodes deep when checking for focus. */ + for (i = 0; i < 2; i++) + { + hr = get_focused_uia_node(node, &node2); + if (FAILED(hr)) + goto exit; + + if (!node2) + break; + + UiaNodeRelease(node); + node = node2; + } + + hr = UiaGetUpdatedCache(node, cache_req, NormalizeState_View, NULL, out_req, tree_struct); + if (FAILED(hr)) + WARN("UiaGetUpdatedCache failed with hr %#lx\n", hr); +exit: + UiaNodeRelease(node); + + return hr; }
/***********************************************************************
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 134 +++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 58 ++++++++- 2 files changed, 188 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b010711c2bb..a92f15c6617 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12487,6 +12487,139 @@ static void test_GetRootElement(IUIAutomation *uia_iface) UiaRegisterProviderCallback(NULL); }
+static const struct prov_method_sequence node_from_focus_seq1[]; +static const struct prov_method_sequence node_from_focus_seq6[]; +static void test_GetFocusedElement(IUIAutomation *uia_iface) +{ + struct Provider_prop_override prop_override; + IUIAutomationCacheRequest *cache_req; + IUIAutomationElement *element; + HRESULT hr; + VARIANT v; + HWND hwnd; + int i; + + hwnd = create_test_hwnd("test_GetFocusedElement class"); + UiaRegisterProviderCallback(test_uia_provider_callback); + + cache_req = NULL; + hr = IUIAutomation_CreateCacheRequest(uia_iface, &cache_req); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cache_req, "cache_req == NULL\n"); + + /* Set clientside providers for our test window and the desktop. */ + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + + set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + prov_root = &Provider.IRawElementProviderSimple_iface; + Provider.ignore_hwnd_prop = TRUE; + + /* NULL input argument tests. */ + hr = IUIAutomation_GetFocusedElement(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IUIAutomation_GetFocusedElementBuildCache(uia_iface, cache_req, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + element = (void *)0xdeadbeef; + hr = IUIAutomation_GetFocusedElementBuildCache(uia_iface, NULL, &element); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(!element, "element != NULL\n"); + + /* Test GetFocusedElement. */ + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = IUIAutomation_GetFocusedElement(uia_iface, &element); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element, "element == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ProviderDescriptionPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), GetDesktopWindow()); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_proxy", TRUE); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", L"Provider_nc", FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider_hwnd", FALSE); + VariantClear(&v); + + ok_method_sequence(node_from_focus_seq1, "node_from_focus_seq1"); + IUIAutomationElement_Release(element); + + /* Test BuildCache variant. */ + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = IUIAutomation_GetFocusedElementBuildCache(uia_iface, cache_req, &element); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!element, "element == NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ProviderDescriptionPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), GetDesktopWindow()); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_proxy", TRUE); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", L"Provider_nc", FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", L"Provider_hwnd", FALSE); + VariantClear(&v); + + ok_method_sequence(node_from_focus_seq1, "node_from_focus_seq1"); + IUIAutomationElement_Release(element); + + /* + * GetFocusedElement returns UIA_E_ELEMENTNOTAVAILABLE when no provider + * matches our view condition, GetFocusedElementBuildCache returns E_FAIL. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider2, &prop_override, 1); + + Provider_nc.focus_prov = NULL; + Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; + Provider.focus_prov = &Provider2.IRawElementProviderFragment_iface; + for (i = 0; i < 2; i++) + { + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + element = (void *)0xdeadbeef; + if (!i) + { + hr = IUIAutomation_GetFocusedElement(uia_iface, &element); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx.\n", hr); + } + else + { + hr = IUIAutomation_GetFocusedElementBuildCache(uia_iface, cache_req, &element); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + } + ok(!element, "element != NULL\n"); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + ok_method_sequence(node_from_focus_seq6, "node_from_focus_seq6"); + } + set_provider_prop_override(&Provider2, NULL, 0); + + base_hwnd_prov = nc_prov = proxy_prov = prov_root = NULL; + IUIAutomationCacheRequest_Release(cache_req); + UiaRegisterProviderCallback(NULL); + DestroyWindow(hwnd); + UnregisterClassA("test_GetFocusedElement class", NULL); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -12594,6 +12727,7 @@ static void test_CUIAutomation(void) test_Element_cache_methods(uia_iface); test_Element_Find(uia_iface); test_GetRootElement(uia_iface); + test_GetFocusedElement(uia_iface);
IUIAutomation_Release(uia_iface); CoUninitialize(); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 6cba9fd46db..bcb23f38c56 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -2816,8 +2816,30 @@ static HRESULT WINAPI uia_iface_ElementFromPoint(IUIAutomation6 *iface, POINT pt
static HRESULT WINAPI uia_iface_GetFocusedElement(IUIAutomation6 *iface, IUIAutomationElement **out_elem) { - FIXME("%p, %p: stub\n", iface, out_elem); - return E_NOTIMPL; + IUIAutomationCacheRequest *cache_req; + HRESULT hr; + + TRACE("%p, %p\n", iface, out_elem); + + if (!out_elem) + return E_POINTER; + + *out_elem = NULL; + hr = create_uia_cache_request_iface(&cache_req); + if (FAILED(hr)) + return hr; + + hr = IUIAutomation6_GetFocusedElementBuildCache(iface, cache_req, out_elem); + IUIAutomationCacheRequest_Release(cache_req); + + /* + * Failure to get a focused element returns E_FAIL from the BuildCache + * method, but UIA_E_ELEMENTNOTAVAILABLE from this one. + */ + if (!(*out_elem) && hr == E_FAIL) + hr = UIA_E_ELEMENTNOTAVAILABLE; + + return hr; }
static HRESULT WINAPI uia_iface_GetRootElementBuildCache(IUIAutomation6 *iface, IUIAutomationCacheRequest *cache_req, @@ -2844,8 +2866,36 @@ static HRESULT WINAPI uia_iface_ElementFromPointBuildCache(IUIAutomation6 *iface static HRESULT WINAPI uia_iface_GetFocusedElementBuildCache(IUIAutomation6 *iface, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **out_elem) { - FIXME("%p, %p, %p: stub\n", iface, cache_req, out_elem); - return E_NOTIMPL; + struct uia_iface *uia_iface = impl_from_IUIAutomation6(iface); + struct UiaCacheRequest *cache_req_struct; + BSTR tree_struct; + SAFEARRAY *sa; + HRESULT hr; + + TRACE("%p, %p, %p\n", iface, cache_req, out_elem); + + if (!out_elem) + return E_POINTER; + + *out_elem = NULL; + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + return hr; + + hr = UiaNodeFromFocus(cache_req_struct, &sa, &tree_struct); + if (SUCCEEDED(hr)) + { + if (!sa) + { + SysFreeString(tree_struct); + return E_FAIL; + } + + hr = create_uia_element_from_cache_req(out_elem, uia_iface->is_cui8, cache_req_struct, 0, sa, tree_struct); + SafeArrayDestroy(sa); + } + + return hr; }
static HRESULT WINAPI uia_iface_CreateTreeWalker(IUIAutomation6 *iface, IUIAutomationCondition *cond,
From: Connor McAdams cmcadams@codeweavers.com
If we pass a node to the provider thread that contains a provider that was created in an STA with the ProviderOptions_UseComThreading flag set, we can deadlock when attempting to get a runtime ID from the proxy. To avoid this, use SendMessageCallback and pump the message queue until we get a result from the provider thread.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 32 +++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 36 +++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a92f15c6617..d41024a6253 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6303,6 +6303,16 @@ static const struct prov_method_sequence node_from_hwnd9[] = { { 0 } };
+static const struct prov_method_sequence node_from_hwnd10[] = { + NODE_CREATE_SEQ(&Provider), + /* Next two only done on Windows 8+. */ + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win11+. */ + { 0 } +}; + static const struct prov_method_sequence disconnect_prov1[] = { { &Provider_child, PROV_GET_PROVIDER_OPTIONS }, /* Win10v1507 and below call this. */ @@ -6574,6 +6584,28 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) Sleep(50); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
+ /* ProviderOptions_UseComThreading test from a separate thread. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + prov_root = &Provider.IRawElementProviderSimple_iface; + initialize_provider(&Provider, ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading, NULL, FALSE); + Provider.frag_root = NULL; + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + hr = UiaNodeFromHandle(hwnd, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + ok_method_sequence(node_from_hwnd10, "node_from_hwnd10"); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + /* Win10v1809 can be slow to call Release on Provider. */ + if (Provider.ref != 1) + Sleep(50); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + if (!pUiaDisconnectProvider) { win_skip("UiaDisconnectProvider not exported by uiautomationcore.dll\n"); diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 76b5c03537a..40df4f53d15 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1941,6 +1941,19 @@ void uia_stop_provider_thread(void) LeaveCriticalSection(&provider_thread_cs); }
+struct uia_lresult_from_node_data { + LRESULT lr; + BOOL returned; +}; + +static void CALLBACK uia_lresult_from_node_callback(HWND hwnd, UINT msg, ULONG_PTR data, LRESULT lresult) +{ + struct uia_lresult_from_node_data *cb_data = (struct uia_lresult_from_node_data *)data; + + cb_data->lr = lresult; + cb_data->returned = TRUE; +} + /* * Pass our IWineUiaNode interface to the provider thread for marshaling. UI * Automation has to work regardless of whether or not COM is initialized on @@ -1948,13 +1961,34 @@ void uia_stop_provider_thread(void) */ LRESULT uia_lresult_from_node(HUIANODE huianode) { + struct uia_lresult_from_node_data cb_data = { 0 }; + if (!uia_start_provider_thread()) { UiaNodeRelease(huianode); return 0; }
- return SendMessageW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, 0, (LPARAM)huianode); + if (!SendMessageCallbackW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, 0, (LPARAM)huianode, + uia_lresult_from_node_callback, (ULONG_PTR)&cb_data)) + { + WARN("SendMessageCallback failed, error %ld\n", GetLastError()); + UiaNodeRelease(huianode); + return 0; + } + + while (!cb_data.returned) + { + MSG msg; + + if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + return cb_data.lr; }
/***********************************************************************
Do we really need so many message sequences for UiaNodeFromFocus? Most of these look almost identical, so I'd think one or two would be enough.
On Fri Apr 21 18:49:33 2023 +0000, Esme Povirk wrote:
Do we really need so many message sequences for UiaNodeFromFocus? Most of these look almost identical, so I'd think one or two would be enough.
The purpose was mostly to show which node providers have GetFocus called on them in different circumstances, and then to show the cache request normalization behavior.
These were written awhile ago, before we began trying to reduce the number of method sequences. It wouldn't hurt anything to remove them, but I also don't know that it'd necessarily help anything either. I'm indifferent towards it. If you'd prefer they be removed we can do that.
On Fri Apr 21 19:03:42 2023 +0000, Connor McAdams wrote:
The purpose was mostly to show which node providers have GetFocus called on them in different circumstances, and then to show the cache request normalization behavior. These were written awhile ago, before we began trying to reduce the number of method sequences. It wouldn't hurt anything to remove them, but I also don't know that it'd necessarily help anything either. I'm indifferent towards it. If you'd prefer they be removed we can do that.
I'll revisit these Monday and try to cut them down/see which ones are even really necessary. :)
I'm not sure about inserting a message loop into a public function. Applications may not expect some messages to be handled there.