From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 23 ++-- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 148 ++++++++++++++++++++- 3 files changed, 158 insertions(+), 14 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 3fb8426a7f5..f64188b0842 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13166,21 +13166,20 @@ static void test_node_from_focus_(struct UiaCacheRequest *cache_req, struct node SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, win_get_obj_count); SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, child_win_get_obj_count); hr = UiaNodeFromFocus(cache_req, &out_req, &tree_struct); - todo_wine ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (exp_node_desc->prov_count) - todo_wine ok_(file, line)(!!out_req, "out_req == NULL\n"); + ok_(file, line)(!!out_req, "out_req == NULL\n"); else ok_(file, line)(!out_req, "out_req != NULL\n"); - todo_wine ok_(file, line)(!!tree_struct, "tree_struct == NULL\n"); + ok_(file, line)(!!tree_struct, "tree_struct == NULL\n"); todo_wine_if(base_hwnd_cback_todo) CHECK_CALLED_MULTI(prov_callback_base_hwnd, base_hwnd_cback_count); todo_wine_if(proxy_cback_todo) CHECK_CALLED_MULTI(prov_callback_proxy, proxy_cback_count); todo_wine_if(nc_cback_todo) CHECK_CALLED_MULTI(prov_callback_nonclient, nc_cback_count); todo_wine_if(win_get_obj_todo) CHECK_CALLED_MULTI(winproc_GETOBJECT_UiaRoot, win_get_obj_count); todo_wine_if(child_win_get_obj_todo) CHECK_CALLED_MULTI(child_winproc_GETOBJECT_UiaRoot, child_win_get_obj_count);
- if (tree_struct) - ok_(file, line)(!wcscmp(tree_struct, exp_tree_struct), "unexpected tree structure %s\n", debugstr_w(tree_struct)); - if (exp_node_desc->prov_count && SUCCEEDED(hr)) + ok_(file, line)(!wcscmp(tree_struct, exp_tree_struct), "unexpected tree structure %s\n", debugstr_w(tree_struct)); + if (exp_node_desc->prov_count) { exp_lbound[0] = exp_lbound[1] = 0; exp_elems[0] = 1; @@ -13250,7 +13249,7 @@ static void test_UiaNodeFromFocus(void) add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc", FALSE); add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd", FALSE);
- test_node_from_focus(&cache_req, &exp_node_desc, 1, 1, 1, 0, 0, TRUE, TRUE, TRUE, FALSE, FALSE); + test_node_from_focus(&cache_req, &exp_node_desc, 1, 1, 1, 0, 0, FALSE, FALSE, FALSE, FALSE, FALSE);
/* Provider_hwnd returns Provider_hwnd2 from GetFocus. */ Provider_hwnd.focus_prov = &Provider_hwnd2.IRawElementProviderFragment_iface; @@ -13260,7 +13259,7 @@ static void test_UiaNodeFromFocus(void) add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE);
- test_node_from_focus(&cache_req, &exp_node_desc, 2, 1, 2, 1, 0, TRUE, TRUE, TRUE, TRUE, FALSE); + test_node_from_focus(&cache_req, &exp_node_desc, 2, 1, 2, 1, 0, TRUE, FALSE, FALSE, FALSE, FALSE);
/* * Provider_proxy returns Provider from GetFocus. The provider that @@ -13277,7 +13276,7 @@ static void test_UiaNodeFromFocus(void) add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE);
- test_node_from_focus(&cache_req, &exp_node_desc, 2, 2, 2, 1, 0, TRUE, TRUE, TRUE, TRUE, FALSE); + test_node_from_focus(&cache_req, &exp_node_desc, 2, 2, 2, 1, 0, TRUE, FALSE, FALSE, FALSE, FALSE);
/* * Provider_nc returns Provider_nc2 from GetFocus, Provider returns @@ -13292,7 +13291,7 @@ static void test_UiaNodeFromFocus(void) init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE);
- test_node_from_focus(&cache_req, &exp_node_desc, 2, 3, 2, 2, 1, TRUE, TRUE, TRUE, TRUE, TRUE); + test_node_from_focus(&cache_req, &exp_node_desc, 2, 3, 2, 2, 1, TRUE, FALSE, FALSE, TRUE, FALSE);
/* * Provider_proxy returns Provider_child_child from GetFocus. The focus @@ -13314,7 +13313,7 @@ static void test_UiaNodeFromFocus(void) set_provider_prop_override(&Provider, &prop_override, 1);
init_node_provider_desc(&exp_node_desc, 0, NULL); - test_node_from_focus(&cache_req, &exp_node_desc, 2, 2, 2, 1, 0, TRUE, TRUE, TRUE, TRUE, FALSE); + test_node_from_focus(&cache_req, &exp_node_desc, 2, 2, 2, 1, 0, TRUE, FALSE, FALSE, FALSE, FALSE);
/* This time, Provider_child matches our view condition. */ set_provider_prop_override(&Provider_child, NULL, 0); @@ -13322,7 +13321,7 @@ static void test_UiaNodeFromFocus(void) init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE);
- test_node_from_focus(&cache_req, &exp_node_desc, 1, 1, 1, 0, 0, TRUE, TRUE, TRUE, FALSE, FALSE); + test_node_from_focus(&cache_req, &exp_node_desc, 1, 1, 1, 0, 0, FALSE, FALSE, FALSE, FALSE, FALSE);
method_sequences_enabled = TRUE; initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE); 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..d9c294ba814 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,83 @@ 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; + + 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; + + while (1) + { + 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; }
/***********************************************************************