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; }
/***********************************************************************