uiautomationcore: Add support for UIA_ProviderDescriptionPropertyId.
[wine.git] / dlls / uiautomationcore / uia_client.c
blobdae78ea8ff446d8a07396354154e66bd5dbc03b5
1 /*
2 * Copyright 2022 Connor McAdams for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "uia_private.h"
21 #include "wine/debug.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
25 static const struct UiaCondition UiaFalseCondition = { ConditionType_False };
27 struct uia_node_array {
28 HUIANODE *nodes;
29 int node_count;
30 SIZE_T node_arr_size;
33 static void clear_node_array(struct uia_node_array *nodes)
35 if (nodes->nodes)
37 int i;
39 for (i = 0; i < nodes->node_count; i++)
40 UiaNodeRelease(nodes->nodes[i]);
42 heap_free(nodes->nodes);
45 memset(nodes, 0, sizeof(*nodes));
48 static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE node)
50 if (!uia_array_reserve((void **)&out_nodes->nodes, &out_nodes->node_arr_size, out_nodes->node_count + 1,
51 sizeof(node)))
52 return E_OUTOFMEMORY;
54 IWineUiaNode_AddRef((IWineUiaNode *)node);
55 out_nodes->nodes[out_nodes->node_count] = node;
56 out_nodes->node_count++;
58 return S_OK;
61 static HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems)
63 LONG ubound;
64 HRESULT hr;
66 *lbound = *elems = 0;
67 hr = SafeArrayGetLBound(sa, dim, lbound);
68 if (FAILED(hr))
69 return hr;
71 hr = SafeArrayGetUBound(sa, dim, &ubound);
72 if (FAILED(hr))
73 return hr;
75 *elems = (ubound - (*lbound)) + 1;
76 return S_OK;
79 HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems)
81 UINT dims;
83 *lbound = *elems = 0;
84 dims = SafeArrayGetDim(sa);
85 if (dims != 1)
87 WARN("Invalid dimensions %d for safearray.\n", dims);
88 return E_FAIL;
91 return get_safearray_dim_bounds(sa, 1, lbound, elems);
94 int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type)
96 LONG i, idx, lbound[2], elems[2];
97 int val[2];
98 HRESULT hr;
100 hr = get_safearray_bounds(sa1, &lbound[0], &elems[0]);
101 if (FAILED(hr))
103 ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr);
104 return -1;
107 hr = get_safearray_bounds(sa2, &lbound[1], &elems[1]);
108 if (FAILED(hr))
110 ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr);
111 return -1;
114 if (elems[0] != elems[1])
115 return (elems[0] > elems[1]) - (elems[0] < elems[1]);
117 if (prop_type != UIAutomationType_IntArray)
119 FIXME("Array type %#x value comparison currently unimplemented.\n", prop_type);
120 return -1;
123 for (i = 0; i < elems[0]; i++)
125 idx = lbound[0] + i;
126 hr = SafeArrayGetElement(sa1, &idx, &val[0]);
127 if (FAILED(hr))
129 ERR("Failed to get element from sa1 with hr %#lx\n", hr);
130 return -1;
133 idx = lbound[1] + i;
134 hr = SafeArrayGetElement(sa2, &idx, &val[1]);
135 if (FAILED(hr))
137 ERR("Failed to get element from sa2 with hr %#lx\n", hr);
138 return -1;
141 if (val[0] != val[1])
142 return (val[0] > val[1]) - (val[0] < val[1]);
145 return 0;
148 static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems)
150 HUIANODE node;
151 HRESULT hr;
152 LONG i;
154 for (i = 0; i < elems; i++)
156 hr = SafeArrayGetElement(sa, &i, &node);
157 if (FAILED(hr))
158 break;
159 UiaNodeRelease(node);
163 static void create_uia_node_safearray(VARIANT *in, VARIANT *out)
165 LONG i, idx, lbound, elems;
166 HUIANODE node;
167 SAFEARRAY *sa;
168 HRESULT hr;
170 if (FAILED(get_safearray_bounds(V_ARRAY(in), &lbound, &elems)))
171 return;
173 if (!(sa = SafeArrayCreateVector(VT_UINT_PTR, 0, elems)))
174 return;
176 for (i = 0; i < elems; i++)
178 IRawElementProviderSimple *elprov;
179 IUnknown *unk;
181 idx = lbound + i;
182 hr = SafeArrayGetElement(V_ARRAY(in), &idx, &unk);
183 if (FAILED(hr))
184 break;
186 hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov);
187 IUnknown_Release(unk);
188 if (FAILED(hr))
189 break;
191 hr = UiaNodeFromProvider(elprov, &node);
192 if (FAILED(hr))
193 break;
195 IRawElementProviderSimple_Release(elprov);
196 hr = SafeArrayPutElement(sa, &i, &node);
197 if (FAILED(hr))
198 break;
201 if (FAILED(hr))
203 clear_uia_node_ptr_safearray(sa, elems);
204 SafeArrayDestroy(sa);
205 return;
208 V_VT(out) = VT_UINT_PTR | VT_ARRAY;
209 V_ARRAY(out) = sa;
212 /* Convert a VT_UINT_PTR SAFEARRAY to VT_UNKNOWN. */
213 static void uia_node_ptr_to_unk_safearray(VARIANT *in)
215 SAFEARRAY *sa = NULL;
216 LONG ubound, i;
217 HUIANODE node;
218 HRESULT hr;
220 hr = SafeArrayGetUBound(V_ARRAY(in), 1, &ubound);
221 if (FAILED(hr))
222 goto exit;
224 if (!(sa = SafeArrayCreateVector(VT_UNKNOWN, 0, ubound + 1)))
226 hr = E_FAIL;
227 goto exit;
230 for (i = 0; i < (ubound + 1); i++)
232 hr = SafeArrayGetElement(V_ARRAY(in), &i, &node);
233 if (FAILED(hr))
234 break;
236 hr = SafeArrayPutElement(sa, &i, node);
237 if (FAILED(hr))
238 break;
240 UiaNodeRelease(node);
243 exit:
244 if (FAILED(hr))
246 clear_uia_node_ptr_safearray(V_ARRAY(in), ubound + 1);
247 if (sa)
248 SafeArrayDestroy(sa);
251 VariantClear(in);
252 if (SUCCEEDED(hr))
254 V_VT(in) = VT_UNKNOWN | VT_ARRAY;
255 V_ARRAY(in) = sa;
259 static HRESULT get_global_interface_table(IGlobalInterfaceTable **git)
261 HRESULT hr;
263 hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL,
264 CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git);
265 if (FAILED(hr))
266 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr);
268 return hr;
271 static HWND get_hwnd_from_provider(IRawElementProviderSimple *elprov)
273 IRawElementProviderSimple *host_prov;
274 HRESULT hr;
275 VARIANT v;
276 HWND hwnd;
278 hwnd = NULL;
279 VariantInit(&v);
280 hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &host_prov);
281 if (SUCCEEDED(hr) && host_prov)
283 hr = IRawElementProviderSimple_GetPropertyValue(host_prov, UIA_NativeWindowHandlePropertyId, &v);
284 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
285 hwnd = UlongToHandle(V_I4(&v));
287 VariantClear(&v);
288 IRawElementProviderSimple_Release(host_prov);
291 if (!IsWindow(hwnd))
293 hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_NativeWindowHandlePropertyId, &v);
294 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
295 hwnd = UlongToHandle(V_I4(&v));
296 VariantClear(&v);
299 return hwnd;
302 static IRawElementProviderSimple *get_provider_hwnd_fragment_root(IRawElementProviderSimple *elprov, HWND *hwnd)
304 IRawElementProviderFragmentRoot *elroot, *elroot2;
305 IRawElementProviderSimple *elprov2, *ret;
306 IRawElementProviderFragment *elfrag;
307 HRESULT hr;
308 int depth;
310 *hwnd = NULL;
312 hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
313 if (FAILED(hr) || !elfrag)
314 return NULL;
316 depth = 0;
317 ret = NULL;
318 elroot = elroot2 = NULL;
321 * Recursively walk up the fragment root chain until:
322 * We get a fragment root that has an HWND associated with it.
323 * We get a NULL fragment root.
324 * We get the same fragment root as the current fragment root.
325 * We've gone up the chain ten times.
327 while (depth < 10)
329 hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot);
330 IRawElementProviderFragment_Release(elfrag);
331 if (FAILED(hr) || !elroot || (elroot == elroot2))
332 break;
334 hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderSimple, (void **)&elprov2);
335 if (FAILED(hr) || !elprov2)
336 break;
338 *hwnd = get_hwnd_from_provider(elprov2);
339 if (IsWindow(*hwnd))
341 ret = elprov2;
342 break;
345 hr = IRawElementProviderSimple_QueryInterface(elprov2, &IID_IRawElementProviderFragment, (void **)&elfrag);
346 IRawElementProviderSimple_Release(elprov2);
347 if (FAILED(hr) || !elfrag)
348 break;
350 if (elroot2)
351 IRawElementProviderFragmentRoot_Release(elroot2);
352 elroot2 = elroot;
353 elroot = NULL;
354 depth++;
357 if (elroot)
358 IRawElementProviderFragmentRoot_Release(elroot);
359 if (elroot2)
360 IRawElementProviderFragmentRoot_Release(elroot2);
362 return ret;
365 int get_node_provider_type_at_idx(struct uia_node *node, int idx)
367 int i, prov_idx;
369 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
371 if (node->prov[i])
373 if (prov_idx == idx)
374 return i;
375 else
376 prov_idx++;
380 ERR("Node %p has no provider at idx %d\n", node, idx);
381 return 0;
384 static HRESULT get_prop_val_from_node_provider(IWineUiaNode *node, const struct uia_prop_info *prop_info, int idx,
385 VARIANT *out_val)
387 IWineUiaProvider *prov;
388 HRESULT hr;
390 VariantInit(out_val);
391 hr = IWineUiaNode_get_provider(node, idx, &prov);
392 if (FAILED(hr))
393 return hr;
395 hr = IWineUiaProvider_get_prop_val(prov, prop_info, out_val);
396 IWineUiaProvider_Release(prov);
398 return hr;
401 static HRESULT get_prov_opts_from_node_provider(IWineUiaNode *node, int idx, int *out_opts)
403 IWineUiaProvider *prov;
404 HRESULT hr;
406 *out_opts = 0;
407 hr = IWineUiaNode_get_provider(node, idx, &prov);
408 if (FAILED(hr))
409 return hr;
411 hr = IWineUiaProvider_get_prov_opts(prov, out_opts);
412 IWineUiaProvider_Release(prov);
414 return hr;
417 static HRESULT get_has_parent_from_node_provider(IWineUiaNode *node, int idx, BOOL *out_val)
419 IWineUiaProvider *prov;
420 HRESULT hr;
422 *out_val = FALSE;
423 hr = IWineUiaNode_get_provider(node, idx, &prov);
424 if (FAILED(hr))
425 return hr;
427 hr = IWineUiaProvider_has_parent(prov, out_val);
428 IWineUiaProvider_Release(prov);
430 return hr;
433 static HRESULT get_navigate_from_node_provider(IWineUiaNode *node, int idx, int nav_dir, VARIANT *ret_val)
435 IWineUiaProvider *prov;
436 HRESULT hr;
438 VariantInit(ret_val);
439 hr = IWineUiaNode_get_provider(node, idx, &prov);
440 if (FAILED(hr))
441 return hr;
443 hr = IWineUiaProvider_navigate(prov, nav_dir, ret_val);
444 IWineUiaProvider_Release(prov);
446 return hr;
450 * IWineUiaNode interface.
452 static HRESULT WINAPI uia_node_QueryInterface(IWineUiaNode *iface, REFIID riid, void **ppv)
454 *ppv = NULL;
455 if (IsEqualIID(riid, &IID_IWineUiaNode) || IsEqualIID(riid, &IID_IUnknown))
456 *ppv = iface;
457 else
458 return E_NOINTERFACE;
460 IWineUiaNode_AddRef(iface);
461 return S_OK;
464 static ULONG WINAPI uia_node_AddRef(IWineUiaNode *iface)
466 struct uia_node *node = impl_from_IWineUiaNode(iface);
467 ULONG ref = InterlockedIncrement(&node->ref);
469 TRACE("%p, refcount %ld\n", node, ref);
470 return ref;
473 static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
475 struct uia_node *node = impl_from_IWineUiaNode(iface);
476 ULONG ref = InterlockedDecrement(&node->ref);
478 TRACE("%p, refcount %ld\n", node, ref);
479 if (!ref)
481 int i;
483 for (i = 0; i < PROV_TYPE_COUNT; i++)
485 if (node->git_cookie[i])
487 IGlobalInterfaceTable *git;
488 HRESULT hr;
490 hr = get_global_interface_table(&git);
491 if (SUCCEEDED(hr))
493 hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[i]);
494 if (FAILED(hr))
495 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
499 if (node->prov[i])
500 IWineUiaProvider_Release(node->prov[i]);
503 if (!list_empty(&node->prov_thread_list_entry))
504 uia_provider_thread_remove_node((HUIANODE)iface);
505 if (node->nested_node)
506 uia_stop_provider_thread();
508 heap_free(node);
511 return ref;
514 static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, int idx, IWineUiaProvider **out_prov)
516 struct uia_node *node = impl_from_IWineUiaNode(iface);
517 int prov_type;
519 TRACE("(%p, %d, %p)\n", iface, idx, out_prov);
521 *out_prov = NULL;
522 if (node->disconnected)
523 return UIA_E_ELEMENTNOTAVAILABLE;
525 if (idx >= node->prov_count)
526 return E_INVALIDARG;
528 prov_type = get_node_provider_type_at_idx(node, idx);
529 if (node->git_cookie[prov_type])
531 IGlobalInterfaceTable *git;
532 IWineUiaProvider *prov;
533 HRESULT hr;
535 hr = get_global_interface_table(&git);
536 if (FAILED(hr))
537 return hr;
539 hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie[prov_type],
540 &IID_IWineUiaProvider, (void **)&prov);
541 if (FAILED(hr))
543 ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr);
544 return hr;
546 *out_prov = prov;
548 else
550 *out_prov = node->prov[prov_type];
551 IWineUiaProvider_AddRef(node->prov[prov_type]);
554 return S_OK;
557 static HRESULT WINAPI uia_node_get_prop_val(IWineUiaNode *iface, const GUID *prop_guid,
558 VARIANT *ret_val)
560 int prop_id = UiaLookupId(AutomationIdentifierType_Property, prop_guid);
561 struct uia_node *node = impl_from_IWineUiaNode(iface);
562 HRESULT hr;
563 VARIANT v;
565 TRACE("%p, %s, %p\n", iface, debugstr_guid(prop_guid), ret_val);
567 if (node->disconnected)
569 VariantInit(ret_val);
570 return UIA_E_ELEMENTNOTAVAILABLE;
573 hr = UiaGetPropertyValue((HUIANODE)iface, prop_id, &v);
575 /* VT_UNKNOWN is UiaGetReservedNotSupported value, no need to marshal it. */
576 if (V_VT(&v) == VT_UNKNOWN)
577 V_VT(ret_val) = VT_EMPTY;
578 else
579 *ret_val = v;
581 return hr;
584 static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface)
586 struct uia_node *node = impl_from_IWineUiaNode(iface);
587 int prov_type;
589 TRACE("%p\n", node);
591 if (node->disconnected)
593 ERR("Attempted to disconnect node which was already disconnected.\n");
594 return E_FAIL;
597 /* Nested nodes can only have one provider. */
598 prov_type = get_node_provider_type_at_idx(node, 0);
599 if (node->git_cookie[prov_type])
601 IGlobalInterfaceTable *git;
602 HRESULT hr;
604 hr = get_global_interface_table(&git);
605 if (SUCCEEDED(hr))
607 hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[prov_type]);
608 if (FAILED(hr))
609 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
611 node->git_cookie[prov_type] = 0;
614 IWineUiaProvider_Release(node->prov[prov_type]);
615 node->prov[prov_type] = NULL;
617 node->disconnected = TRUE;
618 node->prov_count = 0;
620 return S_OK;
623 static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd)
625 struct uia_node *node = impl_from_IWineUiaNode(iface);
627 TRACE("%p, %p\n", node, out_hwnd);
629 *out_hwnd = HandleToUlong(node->hwnd);
631 return S_OK;
634 static const IWineUiaNodeVtbl uia_node_vtbl = {
635 uia_node_QueryInterface,
636 uia_node_AddRef,
637 uia_node_Release,
638 uia_node_get_provider,
639 uia_node_get_prop_val,
640 uia_node_disconnect,
641 uia_node_get_hwnd,
644 static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface)
646 if (!iface || (iface->lpVtbl != &uia_node_vtbl))
647 return NULL;
649 return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface);
652 static BOOL is_nested_node_provider(IWineUiaProvider *iface);
653 static HRESULT prepare_uia_node(struct uia_node *node)
655 int i, prov_idx;
656 HRESULT hr;
658 /* Get the provider index for the provider that created the node. */
659 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
661 if (i == node->creator_prov_type)
663 node->creator_prov_idx = prov_idx;
664 break;
666 else if (node->prov[i])
667 prov_idx++;
671 * HUIANODEs can only have one 'parent link' provider, which handles
672 * parent and sibling navigation for the entire HUIANODE. Each provider is
673 * queried for a parent in descending order, starting with the override
674 * provider. The first provider to have a valid parent is made parent
675 * link. If no providers return a valid parent, the provider at index 0
676 * is made parent link by default.
678 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
680 BOOL has_parent;
682 if (!node->prov[i])
683 continue;
685 hr = get_has_parent_from_node_provider(&node->IWineUiaNode_iface, prov_idx, &has_parent);
686 if (SUCCEEDED(hr) && has_parent)
688 node->parent_link_idx = prov_idx;
689 break;
692 prov_idx++;
695 for (i = 0; i < PROV_TYPE_COUNT; i++)
697 enum ProviderOptions prov_opts;
698 IGlobalInterfaceTable *git;
699 struct uia_provider *prov;
700 HRESULT hr;
702 /* Only regular providers need to be queried for UseComThreading. */
703 if (!node->prov[i] || is_nested_node_provider(node->prov[i]))
704 continue;
706 prov = impl_from_IWineUiaProvider(node->prov[i]);
707 hr = IRawElementProviderSimple_get_ProviderOptions(prov->elprov, &prov_opts);
708 if (FAILED(hr))
709 continue;
712 * If the UseComThreading ProviderOption is specified, all calls to the
713 * provided IRawElementProviderSimple need to respect the apartment type
714 * of the thread that creates the HUIANODE. i.e, if it's created in an
715 * STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
717 if (prov_opts & ProviderOptions_UseComThreading)
719 hr = get_global_interface_table(&git);
720 if (FAILED(hr))
721 return hr;
723 hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
724 &IID_IWineUiaProvider, &node->git_cookie[i]);
725 if (FAILED(hr))
726 return hr;
730 return S_OK;
733 static BOOL node_creator_is_parent_link(struct uia_node *node)
735 if (node->creator_prov_idx == node->parent_link_idx)
736 return TRUE;
737 else
738 return FALSE;
741 static HRESULT get_sibling_from_node_provider(struct uia_node *node, int prov_idx, int nav_dir,
742 VARIANT *out_node)
744 HUIANODE tmp_node;
745 HRESULT hr;
746 VARIANT v;
748 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, prov_idx, nav_dir, &v);
749 if (FAILED(hr))
750 return hr;
752 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
753 if (FAILED(hr))
754 goto exit;
756 while (1)
758 struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)tmp_node);
761 * If our sibling provider is the parent link of it's HUIANODE, then
762 * it is a valid sibling of this node.
764 if (node_creator_is_parent_link(node_data))
765 break;
768 * If our sibling provider is not the parent link of it's HUIANODE, we
769 * need to try the next sibling.
771 hr = get_navigate_from_node_provider((IWineUiaNode *)tmp_node, node_data->creator_prov_idx, nav_dir, &v);
772 UiaNodeRelease(tmp_node);
773 if (FAILED(hr))
774 return hr;
776 tmp_node = NULL;
777 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
778 if (FAILED(hr))
779 break;
782 exit:
783 if (tmp_node)
784 *out_node = v;
786 return S_OK;
789 static HRESULT get_child_for_node(struct uia_node *node, int start_prov_idx, int nav_dir, VARIANT *out_node)
791 int prov_idx = start_prov_idx;
792 HUIANODE tmp_node = NULL;
793 HRESULT hr;
794 VARIANT v;
796 while ((prov_idx >= 0) && (prov_idx < node->prov_count))
798 struct uia_node *node_data;
800 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, prov_idx, nav_dir, &v);
801 if (FAILED(hr))
802 return hr;
804 if (nav_dir == NavigateDirection_FirstChild)
805 prov_idx--;
806 else
807 prov_idx++;
809 /* If we failed to get a child, try the next provider. */
810 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
811 if (FAILED(hr))
812 continue;
814 node_data = impl_from_IWineUiaNode((IWineUiaNode *)tmp_node);
816 /* This child is the parent link of its node, so we can return it. */
817 if (node_creator_is_parent_link(node_data))
818 break;
821 * If the first child provider isn't the parent link of it's HUIANODE,
822 * we need to check the child provider for any valid siblings.
824 if (nav_dir == NavigateDirection_FirstChild)
825 hr = get_sibling_from_node_provider(node_data, node_data->creator_prov_idx,
826 NavigateDirection_NextSibling, &v);
827 else
828 hr = get_sibling_from_node_provider(node_data, node_data->creator_prov_idx,
829 NavigateDirection_PreviousSibling, &v);
831 UiaNodeRelease(tmp_node);
832 if (FAILED(hr))
833 return hr;
835 /* If we got a valid sibling from the child provider, return it. */
836 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
837 if (SUCCEEDED(hr))
838 break;
841 if (tmp_node)
842 *out_node = v;
844 return S_OK;
847 static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node)
849 HRESULT hr;
850 VARIANT v;
852 *out_node = NULL;
854 VariantInit(&v);
855 switch (nav_dir)
857 case NavigateDirection_FirstChild:
858 case NavigateDirection_LastChild:
859 /* First child always comes from last provider index. */
860 if (nav_dir == NavigateDirection_FirstChild)
861 hr = get_child_for_node(node, node->prov_count - 1, nav_dir, &v);
862 else
863 hr = get_child_for_node(node, 0, nav_dir, &v);
864 if (FAILED(hr))
865 WARN("Child navigation failed with hr %#lx\n", hr);
866 break;
868 case NavigateDirection_NextSibling:
869 case NavigateDirection_PreviousSibling:
871 struct uia_node *node_data;
872 HUIANODE parent;
873 VARIANT tmp;
875 hr = get_sibling_from_node_provider(node, node->parent_link_idx, nav_dir, &v);
876 if (FAILED(hr))
878 WARN("Sibling navigation failed with hr %#lx\n", hr);
879 break;
882 if (V_VT(&v) != VT_EMPTY)
883 break;
885 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, node->parent_link_idx,
886 NavigateDirection_Parent, &tmp);
887 if (FAILED(hr))
889 WARN("Parent navigation failed with hr %#lx\n", hr);
890 break;
893 hr = UiaHUiaNodeFromVariant(&tmp, &parent);
894 if (FAILED(hr))
895 break;
898 * If the parent node has multiple providers, attempt to get a sibling
899 * from one of them.
901 node_data = impl_from_IWineUiaNode((IWineUiaNode *)parent);
902 if (node_data->prov_count > 1)
904 if (nav_dir == NavigateDirection_NextSibling)
905 hr = get_child_for_node(node_data, node_data->creator_prov_idx - 1, NavigateDirection_FirstChild, &v);
906 else
907 hr = get_child_for_node(node_data, node_data->creator_prov_idx + 1, NavigateDirection_LastChild, &v);
910 UiaNodeRelease(parent);
911 break;
914 case NavigateDirection_Parent:
915 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, node->parent_link_idx, nav_dir, &v);
916 if (FAILED(hr))
917 WARN("Parent navigation failed with hr %#lx\n", hr);
918 break;
920 default:
921 WARN("Invalid NavigateDirection %d\n", nav_dir);
922 return E_INVALIDARG;
925 if (V_VT(&v) != VT_EMPTY)
927 hr = UiaHUiaNodeFromVariant(&v, (HUIANODE *)out_node);
928 if (FAILED(hr))
929 WARN("UiaHUiaNodeFromVariant failed with hr %#lx\n", hr);
932 return S_OK;
935 static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition);
936 static BOOL uia_condition_matched(HRESULT hr);
939 * Assuming we have a tree that looks like this:
940 * +-------+
941 * | |
942 * | 1 |
943 * | |
944 * +---+---+
946 * +-----------+-----------+
947 * | | |
948 * +---+---+ +---+---+ +---+---+
949 * | | | | | |
950 * | 2 +---| 3 +---+ 4 |
951 * | | | | | |
952 * +---+---+ +-------+ +-------+
954 * +---+---+
955 * | |
956 * | 5 |
957 * | |
958 * +-------+
959 * If we start navigation of the tree from node 1, our visit order for a
960 * depth first search would be 1 -> 2 -> 5 -> 3 -> 4.
962 * However, if we start from the middle of the sequence at node 5 without the
963 * prior context of navigating from nodes 1 and 2, we need to use the function
964 * below to reach node 3, so we can visit the nodes within the tree in the
965 * same order of 5 -> 3 -> 4.
967 static HRESULT traverse_uia_node_tree_siblings(HUIANODE huianode, struct UiaCondition *ascending_stop_cond,
968 int dir, BOOL at_root_level, HUIANODE *out_node)
970 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
971 HUIANODE node2 = NULL;
972 HRESULT hr;
974 *out_node = NULL;
976 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
977 while (1)
979 hr = navigate_uia_node(node, dir, &node2);
980 if (FAILED(hr) || node2 || !at_root_level)
981 break;
983 hr = navigate_uia_node(node, NavigateDirection_Parent, &node2);
984 if (FAILED(hr) || !node2)
985 break;
987 hr = uia_condition_check(node2, ascending_stop_cond);
988 if (FAILED(hr) || uia_condition_matched(hr))
990 UiaNodeRelease(node2);
991 node2 = NULL;
992 break;
995 IWineUiaNode_Release(&node->IWineUiaNode_iface);
996 node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node2);
999 IWineUiaNode_Release(&node->IWineUiaNode_iface);
1000 *out_node = node2;
1002 return hr;
1006 * This function is used to traverse the 'raw' tree of HUIANODEs using a
1007 * depth-first search. As each node in the tree is visited, it is checked
1008 * against two conditions.
1010 * The first is the view condition, which is a filter for what is considered to
1011 * be a valid node in the current tree 'view'. The view condition essentially
1012 * creates a sort of 'virtual' tree. UI Automation provides three default tree
1013 * views:
1014 * -The 'raw' view has the view condition as ConditionType_True, so the tree
1015 * is completely unfiltered. All nodes are valid.
1016 * -The 'control' view contains only nodes that do not return VARIANT_FALSE
1017 * for UIA_IsControlElementPropertyId.
1018 * -The 'content' view contains only nodes that do not return VARIANT_FALSE
1019 * for both UIA_IsControlElementPropertyId and UIA_IsContentElementPropertyId.
1021 * If the currently visited node is considered to be valid within our view
1022 * condition, it is then checked against the search condition to determine if
1023 * it should be returned.
1025 static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond,
1026 struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond,
1027 struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL find_first,
1028 BOOL *root_found, int max_depth, int *cur_depth, struct uia_node_array *out_nodes)
1030 HUIANODE node = huianode;
1031 HRESULT hr;
1033 while (1)
1035 struct uia_node *node_data = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node);
1036 BOOL incr_depth = FALSE;
1037 HUIANODE node2 = NULL;
1039 hr = uia_condition_check(node, view_cond);
1040 if (FAILED(hr))
1041 break;
1043 if (uia_condition_matched(hr))
1046 * If this is a valid node within our treeview, we need to increment
1047 * our current depth within the tree.
1049 incr_depth = TRUE;
1051 if (*root_found)
1053 hr = uia_condition_check(node, search_cond);
1054 if (FAILED(hr))
1055 break;
1057 if (uia_condition_matched(hr))
1059 hr = add_node_to_node_array(out_nodes, node);
1060 if (FAILED(hr))
1061 break;
1063 if (find_first)
1065 hr = S_FALSE;
1066 break;
1071 *root_found = TRUE;
1074 if (incr_depth)
1075 (*cur_depth)++;
1077 /* If we haven't exceeded our maximum traversal depth, visit children of this node. */
1078 if (max_depth < 0 || (*cur_depth) <= max_depth)
1080 if (traversal_opts & TreeTraversalOptions_LastToFirstOrder)
1081 hr = navigate_uia_node(node_data, NavigateDirection_LastChild, &node2);
1082 else
1083 hr = navigate_uia_node(node_data, NavigateDirection_FirstChild, &node2);
1085 if (SUCCEEDED(hr) && node2)
1086 hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond,
1087 traversal_opts, FALSE, find_first, root_found, max_depth, cur_depth, out_nodes);
1089 if (FAILED(hr) || hr == S_FALSE)
1090 break;
1093 if (incr_depth)
1094 (*cur_depth)--;
1097 * Before attempting to visit any siblings of huianode, make sure
1098 * huianode doesn't match our stop condition.
1100 hr = uia_condition_check(node, pre_sibling_nav_stop_cond);
1101 if (FAILED(hr) || uia_condition_matched(hr))
1102 break;
1104 /* Now, check for any siblings to visit. */
1105 if (traversal_opts & TreeTraversalOptions_LastToFirstOrder)
1106 hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_PreviousSibling,
1107 at_root_level, &node2);
1108 else
1109 hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_NextSibling,
1110 at_root_level, &node2);
1112 if (FAILED(hr) || !node2)
1113 break;
1115 UiaNodeRelease(node);
1116 node = node2;
1119 UiaNodeRelease(node);
1121 return hr;
1124 static HRESULT bstrcat_realloc(BSTR *bstr, const WCHAR *cat_str)
1126 if (!SysReAllocStringLen(bstr, NULL, SysStringLen(*bstr) + lstrlenW(cat_str)))
1128 SysFreeString(*bstr);
1129 *bstr = NULL;
1130 return E_OUTOFMEMORY;
1133 lstrcatW(*bstr, cat_str);
1134 return S_OK;
1137 static const WCHAR *prov_desc_type_str[] = {
1138 L"Override",
1139 L"Main",
1140 L"Nonclient",
1141 L"Hwnd",
1144 static HRESULT get_node_provider_description_string(struct uia_node *node, VARIANT *out_desc)
1146 const struct uia_prop_info *prop_info = uia_prop_info_from_id(UIA_ProviderDescriptionPropertyId);
1147 WCHAR buf[256] = { 0 };
1148 HRESULT hr = S_OK;
1149 BSTR node_desc;
1150 int i;
1152 VariantInit(out_desc);
1155 * If we have a single provider, and it's a nested node provider, we just
1156 * return the string directly from the nested node.
1158 if ((node->prov_count == 1) && is_nested_node_provider(node->prov[get_node_provider_type_at_idx(node, 0)]))
1159 return get_prop_val_from_node_provider(&node->IWineUiaNode_iface, prop_info, 0, out_desc);
1161 wsprintfW(buf, L"[pid:%d,providerId:%#x ", GetCurrentProcessId(), node->hwnd);
1162 if (!(node_desc = SysAllocString(buf)))
1163 return E_OUTOFMEMORY;
1165 for (i = 0; i < node->prov_count; i++)
1167 int prov_type = get_node_provider_type_at_idx(node, i);
1168 VARIANT v;
1170 buf[0] = 0;
1171 /* There's a provider preceding this one, add a "; " separator. */
1172 if (i)
1173 lstrcatW(buf, L"; ");
1175 /* Generate the provider type prefix string. */
1176 lstrcatW(buf, prov_desc_type_str[prov_type]);
1177 if (node->parent_link_idx == i)
1178 lstrcatW(buf, L"(parent link)");
1179 lstrcatW(buf, L":");
1180 if (is_nested_node_provider(node->prov[prov_type]))
1181 lstrcatW(buf, L"Nested ");
1183 hr = bstrcat_realloc(&node_desc, buf);
1184 if (FAILED(hr))
1185 goto exit;
1187 VariantInit(&v);
1188 hr = get_prop_val_from_node_provider(&node->IWineUiaNode_iface, prop_info, i, &v);
1189 if (FAILED(hr))
1190 goto exit;
1192 hr = bstrcat_realloc(&node_desc, V_BSTR(&v));
1193 VariantClear(&v);
1194 if (FAILED(hr))
1195 goto exit;
1198 hr = bstrcat_realloc(&node_desc, L"]");
1199 if (SUCCEEDED(hr))
1201 V_VT(out_desc) = VT_BSTR;
1202 V_BSTR(out_desc) = node_desc;
1205 exit:
1206 if (FAILED(hr))
1207 SysFreeString(node_desc);
1209 return hr;
1213 * IWineUiaProvider interface.
1215 static HRESULT WINAPI uia_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv)
1217 *ppv = NULL;
1218 if (IsEqualIID(riid, &IID_IWineUiaProvider) || IsEqualIID(riid, &IID_IUnknown))
1219 *ppv = iface;
1220 else
1221 return E_NOINTERFACE;
1223 IWineUiaProvider_AddRef(iface);
1224 return S_OK;
1227 static ULONG WINAPI uia_provider_AddRef(IWineUiaProvider *iface)
1229 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1230 ULONG ref = InterlockedIncrement(&prov->ref);
1232 TRACE("%p, refcount %ld\n", prov, ref);
1233 return ref;
1236 static void uia_stop_client_thread(void);
1237 static ULONG WINAPI uia_provider_Release(IWineUiaProvider *iface)
1239 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1240 ULONG ref = InterlockedDecrement(&prov->ref);
1242 TRACE("%p, refcount %ld\n", prov, ref);
1243 if (!ref)
1245 IRawElementProviderSimple_Release(prov->elprov);
1246 heap_free(prov);
1249 return ref;
1252 static void get_variant_for_node(HUIANODE node, VARIANT *v)
1254 #ifdef _WIN64
1255 V_VT(v) = VT_I8;
1256 V_I8(v) = (UINT64)node;
1257 #else
1258 V_VT(v) = VT_I4;
1259 V_I4(v) = (UINT32)node;
1260 #endif
1263 static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BOOL out_nested,
1264 VARIANT *v)
1266 HUIANODE node;
1267 HRESULT hr;
1269 VariantInit(v);
1270 hr = create_uia_node_from_elprov(elprov, &node, !out_nested);
1271 IRawElementProviderSimple_Release(elprov);
1272 if (SUCCEEDED(hr))
1274 if (out_nested)
1276 LRESULT lr = uia_lresult_from_node(node);
1278 if (!lr)
1279 return E_FAIL;
1281 V_VT(v) = VT_I4;
1282 V_I4(v) = lr;
1284 else
1285 get_variant_for_node(node, v);
1288 return S_OK;
1291 static HRESULT uia_provider_get_elem_prop_val(struct uia_provider *prov,
1292 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1294 HRESULT hr;
1295 VARIANT v;
1297 VariantInit(&v);
1298 hr = IRawElementProviderSimple_GetPropertyValue(prov->elprov, prop_info->prop_id, &v);
1299 if (FAILED(hr))
1300 goto exit;
1302 switch (prop_info->type)
1304 case UIAutomationType_Int:
1305 if (V_VT(&v) != VT_I4)
1307 WARN("Invalid vt %d for UIAutomationType_Int\n", V_VT(&v));
1308 goto exit;
1310 *ret_val = v;
1311 break;
1313 case UIAutomationType_IntArray:
1314 if (V_VT(&v) != (VT_I4 | VT_ARRAY))
1316 WARN("Invalid vt %d for UIAutomationType_IntArray\n", V_VT(&v));
1317 goto exit;
1319 *ret_val = v;
1320 break;
1322 case UIAutomationType_Double:
1323 if (V_VT(&v) != VT_R8)
1325 WARN("Invalid vt %d for UIAutomationType_Double\n", V_VT(&v));
1326 goto exit;
1328 *ret_val = v;
1329 break;
1331 case UIAutomationType_DoubleArray:
1332 if (V_VT(&v) != (VT_R8 | VT_ARRAY))
1334 WARN("Invalid vt %d for UIAutomationType_DoubleArray\n", V_VT(&v));
1335 goto exit;
1337 *ret_val = v;
1338 break;
1340 case UIAutomationType_Bool:
1341 if (V_VT(&v) != VT_BOOL)
1343 WARN("Invalid vt %d for UIAutomationType_Bool\n", V_VT(&v));
1344 goto exit;
1346 *ret_val = v;
1347 break;
1349 case UIAutomationType_String:
1350 if (V_VT(&v) != VT_BSTR)
1352 WARN("Invalid vt %d for UIAutomationType_String\n", V_VT(&v));
1353 goto exit;
1355 *ret_val = v;
1356 break;
1358 case UIAutomationType_Element:
1360 IRawElementProviderSimple *elprov;
1362 if (V_VT(&v) != VT_UNKNOWN)
1364 WARN("Invalid vt %d for UIAutomationType_Element\n", V_VT(&v));
1365 goto exit;
1368 hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IRawElementProviderSimple,
1369 (void **)&elprov);
1370 VariantClear(&v);
1371 if (FAILED(hr))
1372 goto exit;
1374 hr = get_variant_for_elprov_node(elprov, prov->return_nested_node, ret_val);
1375 if (FAILED(hr))
1376 return hr;
1378 break;
1381 case UIAutomationType_ElementArray:
1382 if (V_VT(&v) != (VT_UNKNOWN | VT_ARRAY))
1384 WARN("Invalid vt %d for UIAutomationType_ElementArray\n", V_VT(&v));
1385 goto exit;
1387 create_uia_node_safearray(&v, ret_val);
1388 if (V_VT(ret_val) == (VT_UINT_PTR | VT_ARRAY))
1389 VariantClear(&v);
1390 break;
1392 default:
1393 break;
1396 exit:
1397 if (V_VT(ret_val) == VT_EMPTY)
1398 VariantClear(&v);
1400 return S_OK;
1403 static SAFEARRAY *append_uia_runtime_id(SAFEARRAY *sa, HWND hwnd, enum ProviderOptions root_opts);
1404 static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov,
1405 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1407 HRESULT hr;
1409 switch (prop_info->prop_id)
1411 case UIA_RuntimeIdPropertyId:
1413 IRawElementProviderFragment *elfrag;
1414 SAFEARRAY *sa;
1415 LONG lbound;
1416 int val;
1418 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1419 if (FAILED(hr) || !elfrag)
1420 break;
1422 hr = IRawElementProviderFragment_GetRuntimeId(elfrag, &sa);
1423 IRawElementProviderFragment_Release(elfrag);
1424 if (FAILED(hr) || !sa)
1425 break;
1427 hr = SafeArrayGetLBound(sa, 1, &lbound);
1428 if (FAILED(hr))
1430 SafeArrayDestroy(sa);
1431 break;
1434 hr = SafeArrayGetElement(sa, &lbound, &val);
1435 if (FAILED(hr))
1437 SafeArrayDestroy(sa);
1438 break;
1441 if (val == UiaAppendRuntimeId)
1443 enum ProviderOptions prov_opts = 0;
1444 IRawElementProviderSimple *elprov;
1445 HWND hwnd;
1447 elprov = get_provider_hwnd_fragment_root(prov->elprov, &hwnd);
1448 if (!elprov)
1450 SafeArrayDestroy(sa);
1451 return E_FAIL;
1454 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1455 IRawElementProviderSimple_Release(elprov);
1456 if (FAILED(hr))
1457 WARN("get_ProviderOptions for root provider failed with %#lx\n", hr);
1459 if (!(sa = append_uia_runtime_id(sa, hwnd, prov_opts)))
1460 break;
1463 V_VT(ret_val) = VT_I4 | VT_ARRAY;
1464 V_ARRAY(ret_val) = sa;
1465 break;
1468 case UIA_BoundingRectanglePropertyId:
1470 IRawElementProviderFragment *elfrag;
1471 struct UiaRect rect = { 0 };
1472 double rect_vals[4];
1473 SAFEARRAY *sa;
1474 LONG idx;
1476 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1477 if (FAILED(hr) || !elfrag)
1478 break;
1480 hr = IRawElementProviderFragment_get_BoundingRectangle(elfrag, &rect);
1481 IRawElementProviderFragment_Release(elfrag);
1482 if (FAILED(hr) || (rect.width <= 0 || rect.height <= 0))
1483 break;
1485 if (!(sa = SafeArrayCreateVector(VT_R8, 0, ARRAY_SIZE(rect_vals))))
1486 break;
1488 rect_vals[0] = rect.left;
1489 rect_vals[1] = rect.top;
1490 rect_vals[2] = rect.width;
1491 rect_vals[3] = rect.height;
1492 for (idx = 0; idx < ARRAY_SIZE(rect_vals); idx++)
1494 hr = SafeArrayPutElement(sa, &idx, &rect_vals[idx]);
1495 if (FAILED(hr))
1497 SafeArrayDestroy(sa);
1498 break;
1502 V_VT(ret_val) = VT_R8 | VT_ARRAY;
1503 V_ARRAY(ret_val) = sa;
1504 break;
1507 case UIA_ProviderDescriptionPropertyId:
1509 /* FIXME: Get actual name of the executable our provider comes from. */
1510 static const WCHAR *provider_origin = L" (unmanaged:uiautomationcore.dll)";
1511 static const WCHAR *default_desc = L"Unidentified provider";
1512 BSTR prov_desc_str;
1513 VARIANT v;
1515 hr = uia_provider_get_elem_prop_val(prov, prop_info, &v);
1516 if (FAILED(hr))
1517 return hr;
1519 if (V_VT(&v) == VT_BSTR)
1520 prov_desc_str = SysAllocStringLen(V_BSTR(&v), lstrlenW(V_BSTR(&v)) + lstrlenW(provider_origin));
1521 else
1522 prov_desc_str = SysAllocStringLen(default_desc, lstrlenW(default_desc) + lstrlenW(provider_origin));
1524 VariantClear(&v);
1525 if (!prov_desc_str)
1526 return E_OUTOFMEMORY;
1528 /* Append the name of the executable our provider comes from. */
1529 wsprintfW(&prov_desc_str[lstrlenW(prov_desc_str)], L"%s", provider_origin);
1530 V_VT(ret_val) = VT_BSTR;
1531 V_BSTR(ret_val) = prov_desc_str;
1532 break;
1535 default:
1536 break;
1539 return S_OK;
1542 static HRESULT uia_provider_get_pattern_prop_val(struct uia_provider *prov,
1543 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1545 const struct uia_pattern_info *pattern_info = uia_pattern_info_from_id(prop_info->pattern_id);
1546 IUnknown *unk, *pattern_prov;
1547 HRESULT hr;
1549 unk = pattern_prov = NULL;
1550 hr = IRawElementProviderSimple_GetPatternProvider(prov->elprov, prop_info->pattern_id, &unk);
1551 if (FAILED(hr) || !unk)
1552 return S_OK;
1554 hr = IUnknown_QueryInterface(unk, pattern_info->pattern_iid, (void **)&pattern_prov);
1555 IUnknown_Release(unk);
1556 if (FAILED(hr) || !pattern_prov)
1558 WARN("Failed to get pattern interface from object\n");
1559 return S_OK;
1562 switch (prop_info->prop_id)
1564 case UIA_ValueIsReadOnlyPropertyId:
1566 BOOL val;
1568 hr = IValueProvider_get_IsReadOnly((IValueProvider *)pattern_prov, &val);
1569 if (SUCCEEDED(hr))
1570 variant_init_bool(ret_val, val);
1571 break;
1574 default:
1575 break;
1578 IUnknown_Release(pattern_prov);
1579 return S_OK;
1582 static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface,
1583 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1585 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1587 TRACE("%p, %p, %p\n", iface, prop_info, ret_val);
1589 VariantInit(ret_val);
1590 switch (prop_info->prop_type)
1592 case PROP_TYPE_ELEM_PROP:
1593 return uia_provider_get_elem_prop_val(prov, prop_info, ret_val);
1595 case PROP_TYPE_SPECIAL:
1596 return uia_provider_get_special_prop_val(prov, prop_info, ret_val);
1598 case PROP_TYPE_PATTERN_PROP:
1599 return uia_provider_get_pattern_prop_val(prov, prop_info, ret_val);
1601 default:
1602 break;
1605 return S_OK;
1608 static HRESULT WINAPI uia_provider_get_prov_opts(IWineUiaProvider *iface, int *out_opts)
1610 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1611 enum ProviderOptions prov_opts;
1612 HRESULT hr;
1614 TRACE("%p, %p\n", iface, out_opts);
1616 *out_opts = 0;
1617 hr = IRawElementProviderSimple_get_ProviderOptions(prov->elprov, &prov_opts);
1618 if (SUCCEEDED(hr))
1619 *out_opts = prov_opts;
1621 return S_OK;
1624 static HRESULT WINAPI uia_provider_has_parent(IWineUiaProvider *iface, BOOL *out_val)
1626 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1628 TRACE("%p, %p\n", iface, out_val);
1630 if (!prov->parent_check_ran)
1632 IRawElementProviderFragment *elfrag, *elfrag2;
1633 HRESULT hr;
1635 prov->has_parent = FALSE;
1636 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1637 if (SUCCEEDED(hr) && elfrag)
1639 hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_Parent, &elfrag2);
1640 IRawElementProviderFragment_Release(elfrag);
1641 if (SUCCEEDED(hr) && elfrag2)
1643 prov->has_parent = TRUE;
1644 IRawElementProviderFragment_Release(elfrag2);
1648 prov->parent_check_ran = TRUE;
1651 *out_val = prov->has_parent;
1653 return S_OK;
1656 static HRESULT WINAPI uia_provider_navigate(IWineUiaProvider *iface, int nav_dir, VARIANT *out_val)
1658 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1659 IRawElementProviderFragment *elfrag, *elfrag2;
1660 HRESULT hr;
1662 TRACE("%p, %d, %p\n", iface, nav_dir, out_val);
1664 VariantInit(out_val);
1665 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1666 if (FAILED(hr) || !elfrag)
1667 return S_OK;
1669 hr = IRawElementProviderFragment_Navigate(elfrag, nav_dir, &elfrag2);
1670 IRawElementProviderFragment_Release(elfrag);
1671 if (SUCCEEDED(hr) && elfrag2)
1673 IRawElementProviderSimple *elprov;
1675 hr = IRawElementProviderFragment_QueryInterface(elfrag2, &IID_IRawElementProviderSimple, (void **)&elprov);
1676 IRawElementProviderFragment_Release(elfrag2);
1677 if (FAILED(hr) || !elprov)
1678 return hr;
1680 hr = get_variant_for_elprov_node(elprov, prov->return_nested_node, out_val);
1681 if (FAILED(hr))
1682 return hr;
1685 return S_OK;
1688 static const IWineUiaProviderVtbl uia_provider_vtbl = {
1689 uia_provider_QueryInterface,
1690 uia_provider_AddRef,
1691 uia_provider_Release,
1692 uia_provider_get_prop_val,
1693 uia_provider_get_prov_opts,
1694 uia_provider_has_parent,
1695 uia_provider_navigate,
1698 static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov,
1699 int prov_type)
1701 struct uia_provider *prov = heap_alloc_zero(sizeof(*prov));
1703 if (!prov)
1704 return E_OUTOFMEMORY;
1706 prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
1707 prov->elprov = elprov;
1708 prov->ref = 1;
1709 node->prov[prov_type] = &prov->IWineUiaProvider_iface;
1710 if (!node->prov_count)
1711 node->creator_prov_type = prov_type;
1712 node->prov_count++;
1714 IRawElementProviderSimple_AddRef(elprov);
1715 return S_OK;
1718 static HRESULT uia_get_providers_for_hwnd(struct uia_node *node);
1719 HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node,
1720 BOOL get_hwnd_providers)
1722 static const int unsupported_prov_opts = ProviderOptions_ProviderOwnsSetFocus | ProviderOptions_HasNativeIAccessible |
1723 ProviderOptions_UseClientCoordinates;
1724 enum ProviderOptions prov_opts;
1725 struct uia_node *node;
1726 int prov_type;
1727 HRESULT hr;
1729 *out_node = NULL;
1731 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1732 if (FAILED(hr))
1733 return hr;
1735 if (prov_opts & unsupported_prov_opts)
1736 FIXME("Ignoring unsupported ProviderOption(s) %#x\n", prov_opts & unsupported_prov_opts);
1738 if (prov_opts & ProviderOptions_OverrideProvider)
1739 prov_type = PROV_TYPE_OVERRIDE;
1740 else if (prov_opts & ProviderOptions_NonClientAreaProvider)
1741 prov_type = PROV_TYPE_NONCLIENT;
1742 else if (prov_opts & ProviderOptions_ServerSideProvider)
1743 prov_type = PROV_TYPE_MAIN;
1744 else if (prov_opts & ProviderOptions_ClientSideProvider)
1745 prov_type = PROV_TYPE_HWND;
1746 else
1747 prov_type = PROV_TYPE_MAIN;
1749 node = heap_alloc_zero(sizeof(*node));
1750 if (!node)
1751 return E_OUTOFMEMORY;
1753 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
1754 node->hwnd = get_hwnd_from_provider(elprov);
1755 list_init(&node->prov_thread_list_entry);
1756 list_init(&node->node_map_list_entry);
1757 node->ref = 1;
1759 hr = create_wine_uia_provider(node, elprov, prov_type);
1760 if (FAILED(hr))
1762 heap_free(node);
1763 return hr;
1766 if (node->hwnd && get_hwnd_providers)
1768 hr = uia_get_providers_for_hwnd(node);
1769 if (FAILED(hr))
1770 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr);
1773 hr = prepare_uia_node(node);
1774 if (FAILED(hr))
1776 IWineUiaNode_Release(&node->IWineUiaNode_iface);
1777 return hr;
1780 *out_node = (void *)&node->IWineUiaNode_iface;
1782 return S_OK;
1785 /***********************************************************************
1786 * UiaNodeFromProvider (uiautomationcore.@)
1788 HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *huianode)
1790 TRACE("(%p, %p)\n", elprov, huianode);
1792 if (!elprov || !huianode)
1793 return E_INVALIDARG;
1795 return create_uia_node_from_elprov(elprov, huianode, TRUE);
1799 * UI Automation client thread functions.
1801 struct uia_client_thread
1803 CO_MTA_USAGE_COOKIE mta_cookie;
1804 HANDLE hthread;
1805 HWND hwnd;
1806 LONG ref;
1809 struct uia_get_node_prov_args {
1810 LRESULT lr;
1811 BOOL unwrap;
1814 static struct uia_client_thread client_thread;
1815 static CRITICAL_SECTION client_thread_cs;
1816 static CRITICAL_SECTION_DEBUG client_thread_cs_debug =
1818 0, 0, &client_thread_cs,
1819 { &client_thread_cs_debug.ProcessLocksList, &client_thread_cs_debug.ProcessLocksList },
1820 0, 0, { (DWORD_PTR)(__FILE__ ": client_thread_cs") }
1822 static CRITICAL_SECTION client_thread_cs = { &client_thread_cs_debug, -1, 0, 0, 0, 0 };
1824 #define WM_UIA_CLIENT_GET_NODE_PROV (WM_USER + 1)
1825 #define WM_UIA_CLIENT_THREAD_STOP (WM_USER + 2)
1826 static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, BOOL unwrap);
1827 static LRESULT CALLBACK uia_client_thread_msg_proc(HWND hwnd, UINT msg, WPARAM wparam,
1828 LPARAM lparam)
1830 switch (msg)
1832 case WM_UIA_CLIENT_GET_NODE_PROV:
1834 struct uia_get_node_prov_args *args = (struct uia_get_node_prov_args *)wparam;
1835 return create_wine_uia_nested_node_provider((struct uia_node *)lparam, args->lr, args->unwrap);
1838 default:
1839 break;
1842 return DefWindowProcW(hwnd, msg, wparam, lparam);
1845 static DWORD WINAPI uia_client_thread_proc(void *arg)
1847 HANDLE initialized_event = arg;
1848 HWND hwnd;
1849 MSG msg;
1851 hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
1852 if (!hwnd)
1854 WARN("CreateWindow failed: %ld\n", GetLastError());
1855 FreeLibraryAndExitThread(huia_module, 1);
1858 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)uia_client_thread_msg_proc);
1859 client_thread.hwnd = hwnd;
1861 /* Initialization complete, thread can now process window messages. */
1862 SetEvent(initialized_event);
1863 TRACE("Client thread started.\n");
1864 while (GetMessageW(&msg, NULL, 0, 0))
1866 if (msg.message == WM_UIA_CLIENT_THREAD_STOP)
1867 break;
1868 TranslateMessage(&msg);
1869 DispatchMessageW(&msg);
1872 TRACE("Shutting down UI Automation client thread.\n");
1874 DestroyWindow(hwnd);
1875 FreeLibraryAndExitThread(huia_module, 0);
1878 static BOOL uia_start_client_thread(void)
1880 BOOL started = TRUE;
1882 EnterCriticalSection(&client_thread_cs);
1883 if (++client_thread.ref == 1)
1885 HANDLE ready_event = NULL;
1886 HANDLE events[2];
1887 HMODULE hmodule;
1888 DWORD wait_obj;
1889 HRESULT hr;
1892 * We use CoIncrementMTAUsage here instead of CoInitialize because it
1893 * allows us to exit the implicit MTA immediately when the thread
1894 * reference count hits 0, rather than waiting for the thread to
1895 * shutdown and call CoUninitialize like the provider thread.
1897 hr = CoIncrementMTAUsage(&client_thread.mta_cookie);
1898 if (FAILED(hr))
1900 started = FALSE;
1901 goto exit;
1904 /* Increment DLL reference count. */
1905 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
1906 (const WCHAR *)uia_start_client_thread, &hmodule);
1908 events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1909 if (!(client_thread.hthread = CreateThread(NULL, 0, uia_client_thread_proc,
1910 ready_event, 0, NULL)))
1912 FreeLibrary(hmodule);
1913 started = FALSE;
1914 goto exit;
1917 events[1] = client_thread.hthread;
1918 wait_obj = WaitForMultipleObjects(2, events, FALSE, INFINITE);
1919 if (wait_obj != WAIT_OBJECT_0)
1921 CloseHandle(client_thread.hthread);
1922 started = FALSE;
1925 exit:
1926 if (ready_event)
1927 CloseHandle(ready_event);
1928 if (!started)
1930 WARN("Failed to start client thread\n");
1931 if (client_thread.mta_cookie)
1932 CoDecrementMTAUsage(client_thread.mta_cookie);
1933 memset(&client_thread, 0, sizeof(client_thread));
1937 LeaveCriticalSection(&client_thread_cs);
1938 return started;
1941 static void uia_stop_client_thread(void)
1943 EnterCriticalSection(&client_thread_cs);
1944 if (!--client_thread.ref)
1946 PostMessageW(client_thread.hwnd, WM_UIA_CLIENT_THREAD_STOP, 0, 0);
1947 CoDecrementMTAUsage(client_thread.mta_cookie);
1948 CloseHandle(client_thread.hthread);
1949 memset(&client_thread, 0, sizeof(client_thread));
1951 LeaveCriticalSection(&client_thread_cs);
1955 * IWineUiaProvider interface for nested node providers.
1957 * Nested node providers represent an HUIANODE that resides in another
1958 * thread or process. We retrieve values from this HUIANODE through the
1959 * IWineUiaNode interface.
1961 struct uia_nested_node_provider {
1962 IWineUiaProvider IWineUiaProvider_iface;
1963 LONG ref;
1965 IWineUiaNode *nested_node;
1968 static inline struct uia_nested_node_provider *impl_from_nested_node_IWineUiaProvider(IWineUiaProvider *iface)
1970 return CONTAINING_RECORD(iface, struct uia_nested_node_provider, IWineUiaProvider_iface);
1973 static HRESULT WINAPI uia_nested_node_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv)
1975 *ppv = NULL;
1976 if (IsEqualIID(riid, &IID_IWineUiaProvider) || IsEqualIID(riid, &IID_IUnknown))
1977 *ppv = iface;
1978 else
1979 return E_NOINTERFACE;
1981 IWineUiaProvider_AddRef(iface);
1982 return S_OK;
1985 static ULONG WINAPI uia_nested_node_provider_AddRef(IWineUiaProvider *iface)
1987 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1988 ULONG ref = InterlockedIncrement(&prov->ref);
1990 TRACE("%p, refcount %ld\n", prov, ref);
1991 return ref;
1994 static ULONG WINAPI uia_nested_node_provider_Release(IWineUiaProvider *iface)
1996 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1997 ULONG ref = InterlockedDecrement(&prov->ref);
1999 TRACE("%p, refcount %ld\n", prov, ref);
2000 if (!ref)
2002 IWineUiaNode_Release(prov->nested_node);
2003 uia_stop_client_thread();
2004 heap_free(prov);
2007 return ref;
2010 static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode);
2011 static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *iface,
2012 const struct uia_prop_info *prop_info, VARIANT *ret_val)
2014 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
2015 HRESULT hr;
2016 VARIANT v;
2018 TRACE("%p, %p, %p\n", iface, prop_info, ret_val);
2020 VariantInit(ret_val);
2021 if (prop_info->type == UIAutomationType_ElementArray)
2023 FIXME("Element array property types currently unsupported for nested nodes.\n");
2024 return E_NOTIMPL;
2027 hr = IWineUiaNode_get_prop_val(prov->nested_node, prop_info->guid, &v);
2028 if (FAILED(hr))
2029 return hr;
2031 switch (prop_info->type)
2033 case UIAutomationType_Element:
2035 HUIANODE node;
2037 hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node);
2038 if (FAILED(hr))
2039 return hr;
2041 get_variant_for_node(node, ret_val);
2042 VariantClear(&v);
2043 break;
2046 default:
2047 *ret_val = v;
2048 break;
2051 return S_OK;
2054 static HRESULT WINAPI uia_nested_node_provider_get_prov_opts(IWineUiaProvider *iface, int *out_opts)
2056 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
2058 TRACE("%p, %p\n", iface, out_opts);
2060 return get_prov_opts_from_node_provider(prov->nested_node, 0, out_opts);
2063 static HRESULT WINAPI uia_nested_node_provider_has_parent(IWineUiaProvider *iface, BOOL *out_val)
2065 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
2067 TRACE("%p, %p\n", iface, out_val);
2069 return get_has_parent_from_node_provider(prov->nested_node, 0, out_val);
2072 static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, int nav_dir, VARIANT *out_val)
2074 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
2075 HUIANODE node;
2076 HRESULT hr;
2077 VARIANT v;
2079 TRACE("%p, %d, %p\n", iface, nav_dir, out_val);
2081 VariantInit(out_val);
2082 hr = get_navigate_from_node_provider(prov->nested_node, 0, nav_dir, &v);
2083 if (FAILED(hr) || V_VT(&v) == VT_EMPTY)
2084 return hr;
2086 hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node);
2087 if (FAILED(hr))
2088 return hr;
2090 get_variant_for_node(node, out_val);
2091 VariantClear(&v);
2093 return S_OK;
2096 static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
2097 uia_nested_node_provider_QueryInterface,
2098 uia_nested_node_provider_AddRef,
2099 uia_nested_node_provider_Release,
2100 uia_nested_node_provider_get_prop_val,
2101 uia_nested_node_provider_get_prov_opts,
2102 uia_nested_node_provider_has_parent,
2103 uia_nested_node_provider_navigate,
2106 static BOOL is_nested_node_provider(IWineUiaProvider *iface)
2108 if (iface->lpVtbl == &uia_nested_node_provider_vtbl)
2109 return TRUE;
2111 return FALSE;
2114 static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr,
2115 BOOL unwrap)
2117 IWineUiaProvider *provider_iface = NULL;
2118 struct uia_nested_node_provider *prov;
2119 IGlobalInterfaceTable *git;
2120 IWineUiaNode *nested_node;
2121 int prov_opts, prov_type;
2122 DWORD git_cookie;
2123 HRESULT hr;
2125 hr = ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&nested_node);
2126 if (FAILED(hr))
2128 uia_stop_client_thread();
2129 return hr;
2132 hr = get_prov_opts_from_node_provider(nested_node, 0, &prov_opts);
2133 if (FAILED(hr))
2135 WARN("Failed to get provider options for node %p with hr %#lx\n", nested_node, hr);
2136 IWineUiaNode_Release(nested_node);
2137 uia_stop_client_thread();
2138 return hr;
2141 /* Nested nodes can only serve as override or main providers. */
2142 if (prov_opts & ProviderOptions_OverrideProvider)
2143 prov_type = PROV_TYPE_OVERRIDE;
2144 else
2145 prov_type = PROV_TYPE_MAIN;
2147 if (node->prov[prov_type])
2149 TRACE("Already have a provider of type %d for this node.\n", prov_type);
2150 IWineUiaNode_Release(nested_node);
2151 uia_stop_client_thread();
2152 return S_OK;
2156 * If we're retrieving a node from an HWND that belongs to the same thread
2157 * as the client making the request, return a normal provider instead of a
2158 * nested node provider.
2160 if (unwrap)
2162 struct uia_node *node_data = unsafe_impl_from_IWineUiaNode(nested_node);
2163 struct uia_provider *prov_data;
2165 if (!node_data)
2167 ERR("Failed to get uia_node structure from nested node\n");
2168 uia_stop_client_thread();
2169 return E_FAIL;
2172 provider_iface = node_data->prov[get_node_provider_type_at_idx(node_data, 0)];
2173 git_cookie = 0;
2175 IWineUiaProvider_AddRef(provider_iface);
2176 prov_data = impl_from_IWineUiaProvider(provider_iface);
2177 prov_data->return_nested_node = FALSE;
2178 prov_data->parent_check_ran = FALSE;
2180 IWineUiaNode_Release(nested_node);
2181 uia_stop_client_thread();
2183 else
2185 prov = heap_alloc_zero(sizeof(*prov));
2186 if (!prov)
2187 return E_OUTOFMEMORY;
2189 prov->IWineUiaProvider_iface.lpVtbl = &uia_nested_node_provider_vtbl;
2190 prov->nested_node = nested_node;
2191 prov->ref = 1;
2192 provider_iface = &prov->IWineUiaProvider_iface;
2195 * We need to use the GIT on all nested node providers so that our
2196 * IWineUiaNode proxy is used in the correct apartment.
2198 hr = get_global_interface_table(&git);
2199 if (FAILED(hr))
2201 IWineUiaProvider_Release(&prov->IWineUiaProvider_iface);
2202 return hr;
2205 hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
2206 &IID_IWineUiaProvider, &git_cookie);
2207 if (FAILED(hr))
2209 IWineUiaProvider_Release(&prov->IWineUiaProvider_iface);
2210 return hr;
2213 if (!node->hwnd)
2215 ULONG hwnd;
2217 hr = IWineUiaNode_get_hwnd(nested_node, &hwnd);
2218 if (SUCCEEDED(hr))
2219 node->hwnd = UlongToHandle(hwnd);
2223 node->prov[prov_type] = provider_iface;
2224 node->git_cookie[prov_type] = git_cookie;
2225 if (!node->prov_count)
2226 node->creator_prov_type = prov_type;
2227 node->prov_count++;
2229 return S_OK;
2232 static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode)
2234 struct uia_node *node;
2235 HRESULT hr;
2237 *huianode = NULL;
2238 node = heap_alloc_zero(sizeof(*node));
2239 if (!node)
2240 return E_OUTOFMEMORY;
2242 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
2243 list_init(&node->prov_thread_list_entry);
2244 list_init(&node->node_map_list_entry);
2245 node->ref = 1;
2247 uia_start_client_thread();
2248 hr = create_wine_uia_nested_node_provider(node, lr, FALSE);
2249 if (FAILED(hr))
2251 heap_free(node);
2252 return hr;
2255 if (node->hwnd)
2257 hr = uia_get_providers_for_hwnd(node);
2258 if (FAILED(hr))
2259 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr);
2262 hr = prepare_uia_node(node);
2263 if (FAILED(hr))
2265 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2266 return hr;
2269 *huianode = (void *)&node->IWineUiaNode_iface;
2271 return hr;
2275 * UiaNodeFromHandle is expected to work even if the calling thread hasn't
2276 * initialized COM. We marshal our node on a separate thread that initializes
2277 * COM for this reason.
2279 static HRESULT uia_get_provider_from_hwnd(struct uia_node *node)
2281 struct uia_get_node_prov_args args;
2283 if (!uia_start_client_thread())
2284 return E_FAIL;
2286 SetLastError(NOERROR);
2287 args.lr = SendMessageW(node->hwnd, WM_GETOBJECT, 0, UiaRootObjectId);
2288 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
2290 uia_stop_client_thread();
2291 return UIA_E_ELEMENTNOTAVAILABLE;
2294 if (!args.lr)
2296 uia_stop_client_thread();
2297 return S_FALSE;
2300 args.unwrap = GetCurrentThreadId() == GetWindowThreadProcessId(node->hwnd, NULL);
2301 return SendMessageW(client_thread.hwnd, WM_UIA_CLIENT_GET_NODE_PROV, (WPARAM)&args, (LPARAM)node);
2304 /***********************************************************************
2305 * UiaNodeFromHandle (uiautomationcore.@)
2307 HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode)
2309 struct uia_node *node;
2310 HRESULT hr;
2312 TRACE("(%p, %p)\n", hwnd, huianode);
2314 if (!huianode)
2315 return E_INVALIDARG;
2317 *huianode = NULL;
2319 if (!IsWindow(hwnd))
2320 return UIA_E_ELEMENTNOTAVAILABLE;
2322 node = heap_alloc_zero(sizeof(*node));
2323 if (!node)
2324 return E_OUTOFMEMORY;
2326 node->hwnd = hwnd;
2327 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
2328 list_init(&node->prov_thread_list_entry);
2329 list_init(&node->node_map_list_entry);
2330 node->ref = 1;
2332 hr = uia_get_providers_for_hwnd(node);
2333 if (FAILED(hr))
2335 heap_free(node);
2336 return hr;
2339 hr = prepare_uia_node(node);
2340 if (FAILED(hr))
2342 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2343 return hr;
2346 *huianode = (void *)&node->IWineUiaNode_iface;
2348 return S_OK;
2351 /***********************************************************************
2352 * UiaNodeRelease (uiautomationcore.@)
2354 BOOL WINAPI UiaNodeRelease(HUIANODE huianode)
2356 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2358 TRACE("(%p)\n", huianode);
2360 if (!node)
2361 return FALSE;
2363 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2364 return TRUE;
2367 static HRESULT get_prop_val_from_node(struct uia_node *node,
2368 const struct uia_prop_info *prop_info, VARIANT *v)
2370 HRESULT hr = S_OK;
2371 int i;
2373 VariantInit(v);
2374 for (i = 0; i < node->prov_count; i++)
2376 hr = get_prop_val_from_node_provider(&node->IWineUiaNode_iface, prop_info, i, v);
2377 if (FAILED(hr) || V_VT(v) != VT_EMPTY)
2378 break;
2381 return hr;
2384 /***********************************************************************
2385 * UiaGetPropertyValue (uiautomationcore.@)
2387 HRESULT WINAPI UiaGetPropertyValue(HUIANODE huianode, PROPERTYID prop_id, VARIANT *out_val)
2389 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2390 const struct uia_prop_info *prop_info;
2391 HRESULT hr;
2392 VARIANT v;
2394 TRACE("(%p, %d, %p)\n", huianode, prop_id, out_val);
2396 if (!node || !out_val)
2397 return E_INVALIDARG;
2399 V_VT(out_val) = VT_UNKNOWN;
2400 UiaGetReservedNotSupportedValue(&V_UNKNOWN(out_val));
2402 prop_info = uia_prop_info_from_id(prop_id);
2403 if (!prop_info)
2404 return E_INVALIDARG;
2406 if (!prop_info->type)
2408 FIXME("No type info for prop_id %d\n", prop_id);
2409 return E_NOTIMPL;
2412 switch (prop_id)
2414 case UIA_RuntimeIdPropertyId:
2416 SAFEARRAY *sa;
2418 hr = UiaGetRuntimeId(huianode, &sa);
2419 if (SUCCEEDED(hr) && sa)
2421 V_VT(out_val) = VT_I4 | VT_ARRAY;
2422 V_ARRAY(out_val) = sa;
2424 return S_OK;
2427 case UIA_ProviderDescriptionPropertyId:
2428 hr = get_node_provider_description_string(node, &v);
2429 if (SUCCEEDED(hr) && (V_VT(&v) == VT_BSTR))
2430 *out_val = v;
2431 return hr;
2433 default:
2434 break;
2437 hr = get_prop_val_from_node(node, prop_info, &v);
2438 if (SUCCEEDED(hr) && V_VT(&v) != VT_EMPTY)
2441 * ElementArray types come back as an array of pointers to prevent the
2442 * HUIANODEs from getting marshaled. We need to convert them to
2443 * VT_UNKNOWN here.
2445 if (prop_info->type == UIAutomationType_ElementArray)
2447 uia_node_ptr_to_unk_safearray(&v);
2448 if (V_VT(&v) != VT_EMPTY)
2449 *out_val = v;
2451 else
2452 *out_val = v;
2455 return hr;
2458 #define UIA_RUNTIME_ID_PREFIX 42
2460 enum fragment_root_prov_type_ids {
2461 FRAGMENT_ROOT_NONCLIENT_TYPE_ID = 0x03,
2462 FRAGMENT_ROOT_MAIN_TYPE_ID = 0x04,
2463 FRAGMENT_ROOT_OVERRIDE_TYPE_ID = 0x05,
2466 static HRESULT write_runtime_id_base(SAFEARRAY *sa, HWND hwnd)
2468 const int rt_id[2] = { UIA_RUNTIME_ID_PREFIX, HandleToUlong(hwnd) };
2469 HRESULT hr;
2470 LONG idx;
2472 for (idx = 0; idx < ARRAY_SIZE(rt_id); idx++)
2474 hr = SafeArrayPutElement(sa, &idx, (void *)&rt_id[idx]);
2475 if (FAILED(hr))
2476 return hr;
2479 return S_OK;
2482 static SAFEARRAY *append_uia_runtime_id(SAFEARRAY *sa, HWND hwnd, enum ProviderOptions root_opts)
2484 LONG i, idx, lbound, elems;
2485 SAFEARRAY *sa2, *ret;
2486 HRESULT hr;
2487 int val;
2489 ret = sa2 = NULL;
2490 hr = get_safearray_bounds(sa, &lbound, &elems);
2491 if (FAILED(hr))
2492 goto exit;
2494 /* elems includes the UiaAppendRuntimeId value, so we only add 2. */
2495 if (!(sa2 = SafeArrayCreateVector(VT_I4, 0, elems + 2)))
2496 goto exit;
2498 hr = write_runtime_id_base(sa2, hwnd);
2499 if (FAILED(hr))
2500 goto exit;
2502 if (root_opts & ProviderOptions_NonClientAreaProvider)
2503 val = FRAGMENT_ROOT_NONCLIENT_TYPE_ID;
2504 else if (root_opts & ProviderOptions_OverrideProvider)
2505 val = FRAGMENT_ROOT_OVERRIDE_TYPE_ID;
2506 else
2507 val = FRAGMENT_ROOT_MAIN_TYPE_ID;
2509 idx = 2;
2510 hr = SafeArrayPutElement(sa2, &idx, &val);
2511 if (FAILED(hr))
2512 goto exit;
2514 for (i = 0; i < (elems - 1); i++)
2516 idx = (lbound + 1) + i;
2517 hr = SafeArrayGetElement(sa, &idx, &val);
2518 if (FAILED(hr))
2519 goto exit;
2521 idx = (3 + i);
2522 hr = SafeArrayPutElement(sa2, &idx, &val);
2523 if (FAILED(hr))
2524 goto exit;
2527 ret = sa2;
2529 exit:
2531 if (!ret)
2532 SafeArrayDestroy(sa2);
2534 SafeArrayDestroy(sa);
2535 return ret;
2538 /***********************************************************************
2539 * UiaGetRuntimeId (uiautomationcore.@)
2541 HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id)
2543 const struct uia_prop_info *prop_info = uia_prop_info_from_id(UIA_RuntimeIdPropertyId);
2544 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2545 HRESULT hr;
2547 TRACE("(%p, %p)\n", huianode, runtime_id);
2549 if (!node || !runtime_id)
2550 return E_INVALIDARG;
2552 *runtime_id = NULL;
2554 /* Provide an HWND based runtime ID if the node has an HWND. */
2555 if (node->hwnd)
2557 SAFEARRAY *sa;
2559 if (!(sa = SafeArrayCreateVector(VT_I4, 0, 2)))
2560 return E_FAIL;
2562 hr = write_runtime_id_base(sa, node->hwnd);
2563 if (FAILED(hr))
2565 SafeArrayDestroy(sa);
2566 return hr;
2569 *runtime_id = sa;
2570 return S_OK;
2572 else
2574 VARIANT v;
2576 hr = get_prop_val_from_node(node, prop_info, &v);
2577 if (FAILED(hr))
2579 VariantClear(&v);
2580 return hr;
2583 if (V_VT(&v) == (VT_I4 | VT_ARRAY))
2584 *runtime_id = V_ARRAY(&v);
2587 return S_OK;
2590 /***********************************************************************
2591 * UiaHUiaNodeFromVariant (uiautomationcore.@)
2593 HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode)
2595 const VARTYPE expected_vt = sizeof(void *) == 8 ? VT_I8 : VT_I4;
2597 TRACE("(%p, %p)\n", in_val, huianode);
2599 if (!in_val || !huianode)
2600 return E_INVALIDARG;
2602 *huianode = NULL;
2603 if ((V_VT(in_val) != expected_vt) && (V_VT(in_val) != VT_UNKNOWN))
2605 WARN("Invalid vt %d\n", V_VT(in_val));
2606 return E_INVALIDARG;
2609 if (V_VT(in_val) == VT_UNKNOWN)
2611 if (V_UNKNOWN(in_val))
2612 IUnknown_AddRef(V_UNKNOWN(in_val));
2613 *huianode = (HUIANODE)V_UNKNOWN(in_val);
2615 else
2617 #ifdef _WIN64
2618 *huianode = (HUIANODE)V_I8(in_val);
2619 #else
2620 *huianode = (HUIANODE)V_I4(in_val);
2621 #endif
2624 return S_OK;
2627 static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderType prov_type)
2629 switch (prov_type)
2631 case ProviderType_Proxy:
2632 FIXME("Default ProviderType_Proxy MSAA provider unimplemented.\n");
2633 break;
2635 case ProviderType_NonClientArea:
2636 FIXME("Default ProviderType_NonClientArea provider unimplemented.\n");
2637 break;
2639 case ProviderType_BaseHwnd:
2640 FIXME("Default ProviderType_BaseHwnd provider unimplemented.\n");
2641 break;
2643 default:
2644 break;
2647 return NULL;
2650 static UiaProviderCallback *uia_provider_callback = default_uia_provider_callback;
2652 static HRESULT uia_get_clientside_provider(struct uia_node *node, int prov_type,
2653 int node_prov_type)
2655 IRawElementProviderSimple *elprov;
2656 LONG lbound, elems;
2657 SAFEARRAY *sa;
2658 IUnknown *unk;
2659 VARTYPE vt;
2660 HRESULT hr;
2662 if (!(sa = uia_provider_callback(node->hwnd, prov_type)))
2663 return S_OK;
2665 hr = SafeArrayGetVartype(sa, &vt);
2666 if (FAILED(hr) || (vt != VT_UNKNOWN))
2667 goto exit;
2669 hr = get_safearray_bounds(sa, &lbound, &elems);
2670 if (FAILED(hr))
2671 goto exit;
2673 /* Returned SAFEARRAY can only have 1 element. */
2674 if (elems != 1)
2676 WARN("Invalid element count %ld for returned SAFEARRAY\n", elems);
2677 goto exit;
2680 hr = SafeArrayGetElement(sa, &lbound, &unk);
2681 if (FAILED(hr))
2682 goto exit;
2684 hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov);
2685 IUnknown_Release(unk);
2686 if (FAILED(hr) || !elprov)
2688 WARN("Failed to get IRawElementProviderSimple from returned SAFEARRAY.\n");
2689 hr = S_OK;
2690 goto exit;
2693 hr = create_wine_uia_provider(node, elprov, node_prov_type);
2694 IRawElementProviderSimple_Release(elprov);
2696 exit:
2697 if (FAILED(hr))
2698 WARN("Failed to get clientside provider, hr %#lx\n", hr);
2699 SafeArrayDestroy(sa);
2700 return hr;
2703 static HRESULT uia_get_providers_for_hwnd(struct uia_node *node)
2705 HRESULT hr;
2707 hr = uia_get_provider_from_hwnd(node);
2708 if (FAILED(hr))
2709 return hr;
2711 if (!node->prov[PROV_TYPE_MAIN])
2713 hr = uia_get_clientside_provider(node, ProviderType_Proxy, PROV_TYPE_MAIN);
2714 if (FAILED(hr))
2715 return hr;
2718 if (!node->prov[PROV_TYPE_OVERRIDE])
2719 FIXME("Override provider callback currently unimplemented.\n");
2721 if (!node->prov[PROV_TYPE_NONCLIENT])
2723 hr = uia_get_clientside_provider(node, ProviderType_NonClientArea, PROV_TYPE_NONCLIENT);
2724 if (FAILED(hr))
2725 return hr;
2728 if (!node->prov[PROV_TYPE_HWND])
2730 hr = uia_get_clientside_provider(node, ProviderType_BaseHwnd, PROV_TYPE_HWND);
2731 if (FAILED(hr))
2732 return hr;
2735 if (!node->prov_count)
2737 if (uia_provider_callback == default_uia_provider_callback)
2738 return E_NOTIMPL;
2739 else
2740 return E_FAIL;
2743 return S_OK;
2746 /***********************************************************************
2747 * UiaRegisterProviderCallback (uiautomationcore.@)
2749 void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback)
2751 TRACE("(%p)\n", callback);
2753 if (callback)
2754 uia_provider_callback = callback;
2755 else
2756 uia_provider_callback = default_uia_provider_callback;
2759 static BOOL uia_condition_matched(HRESULT hr)
2761 if (hr == S_FALSE)
2762 return FALSE;
2763 else
2764 return TRUE;
2767 static HRESULT uia_property_condition_check(HUIANODE node, struct UiaPropertyCondition *prop_cond)
2769 const struct uia_prop_info *prop_info = uia_prop_info_from_id(prop_cond->PropertyId);
2770 HRESULT hr;
2771 VARIANT v;
2773 if (!prop_info)
2774 return E_INVALIDARG;
2776 switch (prop_info->type)
2778 case UIAutomationType_Bool:
2779 case UIAutomationType_IntArray:
2780 break;
2782 default:
2783 FIXME("PropertyCondition comparison unimplemented for type %#x\n", prop_info->type);
2784 return E_NOTIMPL;
2787 hr = UiaGetPropertyValue(node, prop_info->prop_id, &v);
2788 if (FAILED(hr) || V_VT(&v) == VT_UNKNOWN)
2789 return S_FALSE;
2791 if (V_VT(&v) == V_VT(&prop_cond->Value))
2793 switch (prop_info->type)
2795 case UIAutomationType_Bool:
2796 if (V_BOOL(&v) == V_BOOL(&prop_cond->Value))
2797 hr = S_OK;
2798 else
2799 hr = S_FALSE;
2800 break;
2802 case UIAutomationType_IntArray:
2803 if (!uia_compare_safearrays(V_ARRAY(&v), V_ARRAY(&prop_cond->Value), prop_info->type))
2804 hr = S_OK;
2805 else
2806 hr = S_FALSE;
2807 break;
2809 default:
2810 break;
2813 else
2814 hr = S_FALSE;
2816 VariantClear(&v);
2817 return hr;
2820 static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition)
2822 HRESULT hr;
2824 switch (condition->ConditionType)
2826 case ConditionType_True:
2827 return S_OK;
2829 case ConditionType_False:
2830 return S_FALSE;
2832 case ConditionType_Not:
2834 struct UiaNotCondition *not_cond = (struct UiaNotCondition *)condition;
2836 hr = uia_condition_check(node, not_cond->pConditions);
2837 if (FAILED(hr))
2838 return hr;
2840 if (uia_condition_matched(hr))
2841 return S_FALSE;
2842 else
2843 return S_OK;
2846 case ConditionType_And:
2847 case ConditionType_Or:
2849 struct UiaAndOrCondition *and_or_cond = (struct UiaAndOrCondition *)condition;
2850 int i;
2852 for (i = 0; i < and_or_cond->cConditions; i++)
2854 hr = uia_condition_check(node, and_or_cond->ppConditions[i]);
2855 if (FAILED(hr))
2856 return hr;
2858 if (condition->ConditionType == ConditionType_And && !uia_condition_matched(hr))
2859 return S_FALSE;
2860 else if (condition->ConditionType == ConditionType_Or && uia_condition_matched(hr))
2861 return S_OK;
2864 if (condition->ConditionType == ConditionType_Or)
2865 return S_FALSE;
2866 else
2867 return S_OK;
2870 case ConditionType_Property:
2871 return uia_property_condition_check(node, (struct UiaPropertyCondition *)condition);
2873 default:
2874 WARN("Invalid condition type %d\n", condition->ConditionType);
2875 return E_INVALIDARG;
2879 /***********************************************************************
2880 * UiaGetUpdatedCache (uiautomationcore.@)
2882 HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state,
2883 struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct)
2885 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2886 struct UiaCondition *cond;
2887 SAFEARRAYBOUND sabound[2];
2888 SAFEARRAY *sa;
2889 LONG idx[2];
2890 HRESULT hr;
2891 VARIANT v;
2892 int i;
2894 TRACE("(%p, %p, %u, %p, %p, %p)\n", huianode, cache_req, normalize_state, normalize_cond, out_req, tree_struct);
2896 if (!node || !out_req || !tree_struct || !cache_req)
2897 return E_INVALIDARG;
2899 *tree_struct = NULL;
2900 *out_req = NULL;
2902 if (cache_req->Scope != TreeScope_Element)
2904 FIXME("Unsupported cache request scope %#x\n", cache_req->Scope);
2905 return E_NOTIMPL;
2908 if (cache_req->cPatterns && cache_req->pPatterns)
2909 FIXME("Pattern caching currently unimplemented\n");
2911 if (cache_req->cProperties && cache_req->pProperties)
2913 for (i = 0; i < cache_req->cProperties; i++)
2915 if (!uia_prop_info_from_id(cache_req->pProperties[i]))
2916 return E_INVALIDARG;
2920 switch (normalize_state)
2922 case NormalizeState_None:
2923 cond = NULL;
2924 break;
2926 case NormalizeState_View:
2927 cond = cache_req->pViewCondition;
2928 break;
2930 case NormalizeState_Custom:
2931 cond = normalize_cond;
2932 break;
2934 default:
2935 WARN("Invalid normalize_state %d\n", normalize_state);
2936 return E_INVALIDARG;
2939 if (cond)
2941 hr = uia_condition_check(huianode, cond);
2942 if (FAILED(hr))
2943 return hr;
2945 if (!uia_condition_matched(hr))
2947 *tree_struct = SysAllocString(L"");
2948 return S_OK;
2952 sabound[0].cElements = 1;
2953 sabound[1].cElements = 1 + cache_req->cProperties;
2954 sabound[0].lLbound = sabound[1].lLbound = 0;
2955 if (!(sa = SafeArrayCreate(VT_VARIANT, 2, sabound)))
2957 WARN("Failed to create safearray\n");
2958 return E_FAIL;
2961 get_variant_for_node(huianode, &v);
2962 idx[0] = idx[1] = 0;
2964 hr = SafeArrayPutElement(sa, idx, &v);
2965 if (FAILED(hr))
2967 SafeArrayDestroy(sa);
2968 return hr;
2971 idx[0] = 0;
2972 VariantClear(&v);
2973 for (i = 0; i < cache_req->cProperties; i++)
2975 hr = UiaGetPropertyValue(huianode, cache_req->pProperties[i], &v);
2976 /* Don't fail on unimplemented properties. */
2977 if (FAILED(hr) && hr != E_NOTIMPL)
2979 SafeArrayDestroy(sa);
2980 return hr;
2983 idx[1] = 1 + i;
2984 hr = SafeArrayPutElement(sa, idx, &v);
2985 VariantClear(&v);
2986 if (FAILED(hr))
2988 SafeArrayDestroy(sa);
2989 return hr;
2994 * AddRef huianode since we're returning a reference to the same node we
2995 * passed in, rather than creating a new one.
2997 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
2999 *out_req = sa;
3000 *tree_struct = SysAllocString(L"P)");
3002 return S_OK;
3005 /***********************************************************************
3006 * UiaNavigate (uiautomationcore.@)
3008 HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct UiaCondition *nav_condition,
3009 struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct)
3011 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
3012 HUIANODE node2;
3013 HRESULT hr;
3015 TRACE("(%p, %u, %p, %p, %p, %p)\n", huianode, dir, nav_condition, cache_req, out_req,
3016 tree_struct);
3018 if (!node || !nav_condition || !cache_req || !out_req || !tree_struct)
3019 return E_INVALIDARG;
3021 *out_req = NULL;
3022 *tree_struct = NULL;
3024 if (nav_condition->ConditionType != ConditionType_True)
3026 FIXME("ConditionType %d based navigation is not implemented.\n", nav_condition->ConditionType);
3027 return E_NOTIMPL;
3030 hr = navigate_uia_node(node, dir, &node2);
3031 if (FAILED(hr))
3032 return hr;
3034 if (node2)
3036 hr = UiaGetUpdatedCache(node2, cache_req, NormalizeState_None, NULL, out_req, tree_struct);
3037 if (FAILED(hr))
3038 WARN("UiaGetUpdatedCache failed with hr %#lx\n", hr);
3039 UiaNodeRelease(node2);
3042 return hr;
3045 /* Combine multiple cache requests into a single SAFEARRAY. */
3046 static HRESULT uia_cache_request_combine(SAFEARRAY **reqs, int reqs_count, SAFEARRAY *out_req)
3048 LONG idx[2], lbound[2], elems[2], cur_offset;
3049 int i, x, y;
3050 HRESULT hr;
3051 VARIANT v;
3053 for (i = cur_offset = 0; i < reqs_count; i++)
3055 if (!reqs[i])
3056 continue;
3058 for (x = 0; x < 2; x++)
3060 hr = get_safearray_dim_bounds(reqs[i], 1 + x, &lbound[x], &elems[x]);
3061 if (FAILED(hr))
3062 return hr;
3065 for (x = 0; x < elems[0]; x++)
3067 for (y = 0; y < elems[1]; y++)
3069 idx[0] = x + lbound[0];
3070 idx[1] = y + lbound[1];
3071 hr = SafeArrayGetElement(reqs[i], idx, &v);
3072 if (FAILED(hr))
3073 return hr;
3075 idx[0] = x + cur_offset;
3076 idx[1] = y;
3077 hr = SafeArrayPutElement(out_req, idx, &v);
3078 if (FAILED(hr))
3079 return hr;
3083 cur_offset += elems[0];
3086 return S_OK;
3089 /***********************************************************************
3090 * UiaFind (uiautomationcore.@)
3092 HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, struct UiaCacheRequest *cache_req,
3093 SAFEARRAY **out_req, SAFEARRAY **out_offsets, SAFEARRAY **out_tree_structs)
3095 struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId };
3096 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
3097 SAFEARRAY *runtime_id, *req, *offsets, *tree_structs, **tmp_reqs;
3098 struct UiaCondition *sibling_stop_cond;
3099 struct uia_node_array nodes = { 0 };
3100 LONG idx, lbound, elems, cur_offset;
3101 SAFEARRAYBOUND sabound[2];
3102 int i, cur_depth = 0;
3103 BSTR tree_struct;
3104 BOOL root_found;
3105 HRESULT hr;
3107 TRACE("(%p, %p, %p, %p, %p, %p)\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs);
3109 if (!node || !find_params || !cache_req || !out_req || !out_offsets || !out_tree_structs)
3110 return E_INVALIDARG;
3112 *out_tree_structs = *out_offsets = *out_req = tree_structs = offsets = req = NULL;
3113 tmp_reqs = NULL;
3116 * If the initial node has a runtime ID, we'll use it as a stop
3117 * condition.
3119 hr = UiaGetRuntimeId(huianode, &runtime_id);
3120 if (SUCCEEDED(hr) && runtime_id)
3122 V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY;
3123 V_ARRAY(&prop_cond.Value) = runtime_id;
3124 sibling_stop_cond = (struct UiaCondition *)&prop_cond;
3126 else
3127 sibling_stop_cond = (struct UiaCondition *)&UiaFalseCondition;
3129 if (find_params->ExcludeRoot)
3130 root_found = FALSE;
3131 else
3132 root_found = TRUE;
3134 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
3135 hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond,
3136 cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->FindFirst, &root_found,
3137 find_params->MaxDepth, &cur_depth, &nodes);
3138 if (FAILED(hr) || !nodes.node_count)
3139 goto exit;
3141 if (!(offsets = SafeArrayCreateVector(VT_I4, 0, nodes.node_count)))
3143 hr = E_FAIL;
3144 goto exit;
3147 if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, nodes.node_count)))
3149 hr = E_FAIL;
3150 goto exit;
3153 if (!(tmp_reqs = heap_alloc_zero(sizeof(*tmp_reqs) * nodes.node_count)))
3155 hr = E_OUTOFMEMORY;
3156 goto exit;
3160 * Get a count of how many total nodes we'll need to return, as well as
3161 * set the tree structure strings and cache request offsets for our final
3162 * combined SAFEARRAY.
3164 for (i = cur_offset = 0; i < nodes.node_count; i++)
3166 hr = UiaGetUpdatedCache(nodes.nodes[i], cache_req, NormalizeState_None, NULL, &tmp_reqs[i], &tree_struct);
3167 if (FAILED(hr))
3168 goto exit;
3170 idx = i;
3171 hr = SafeArrayPutElement(tree_structs, &idx, tree_struct);
3172 SysFreeString(tree_struct);
3173 if (FAILED(hr))
3174 goto exit;
3176 hr = SafeArrayPutElement(offsets, &idx, &cur_offset);
3177 if (FAILED(hr))
3178 goto exit;
3180 if (!tmp_reqs[i])
3181 continue;
3183 hr = get_safearray_dim_bounds(tmp_reqs[i], 1, &lbound, &elems);
3184 if (FAILED(hr))
3185 goto exit;
3187 cur_offset += elems;
3190 if (nodes.node_count == 1)
3192 req = tmp_reqs[0];
3193 heap_free(tmp_reqs);
3194 tmp_reqs = NULL;
3196 else
3198 sabound[0].lLbound = sabound[1].lLbound = 0;
3199 sabound[0].cElements = cur_offset;
3200 sabound[1].cElements = 1 + cache_req->cProperties + cache_req->cPatterns;
3201 if (!(req = SafeArrayCreate(VT_VARIANT, 2, sabound)))
3203 hr = E_FAIL;
3204 goto exit;
3207 hr = uia_cache_request_combine(tmp_reqs, nodes.node_count, req);
3208 if (FAILED(hr))
3209 goto exit;
3212 *out_tree_structs = tree_structs;
3213 *out_offsets = offsets;
3214 *out_req = req;
3216 exit:
3217 VariantClear(&prop_cond.Value);
3218 clear_node_array(&nodes);
3220 if (tmp_reqs)
3222 for (i = 0; i < nodes.node_count; i++)
3223 SafeArrayDestroy(tmp_reqs[i]);
3224 heap_free(tmp_reqs);
3227 if (FAILED(hr))
3229 SafeArrayDestroy(tree_structs);
3230 SafeArrayDestroy(offsets);
3231 SafeArrayDestroy(req);
3234 return hr;
3237 /***********************************************************************
3238 * UiaAddEvent (uiautomationcore.@)
3240 HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope,
3241 PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent)
3243 FIXME("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count,
3244 cache_req, huiaevent);
3245 return E_NOTIMPL;
3248 /***********************************************************************
3249 * UiaRemoveEvent (uiautomationcore.@)
3251 HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
3253 FIXME("(%p): stub\n", huiaevent);
3254 return E_NOTIMPL;
3257 /***********************************************************************
3258 * UiaEventAddWindow (uiautomationcore.@)
3260 HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd)
3262 FIXME("(%p, %p): stub\n", huiaevent, hwnd);
3263 return E_NOTIMPL;
3266 /***********************************************************************
3267 * UiaEventRemoveWindow (uiautomationcore.@)
3269 HRESULT WINAPI UiaEventRemoveWindow(HUIAEVENT huiaevent, HWND hwnd)
3271 FIXME("(%p, %p): stub\n", huiaevent, hwnd);
3272 return E_NOTIMPL;