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);