kernelbase: Let GetModuleBaseName succeed on 64bit modules in wow64.
[wine.git] / dlls / uiautomationcore / uia_client.c
blob14f9977a4d6e83923c49fe2c2b2148eade41ae83
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"
22 #include "wine/heap.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
26 static const struct UiaCondition UiaFalseCondition = { ConditionType_False };
28 static BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size)
30 SIZE_T max_capacity, new_capacity;
31 void *new_elements;
33 if (count <= *capacity)
34 return TRUE;
36 max_capacity = ~(SIZE_T)0 / size;
37 if (count > max_capacity)
38 return FALSE;
40 new_capacity = max(1, *capacity);
41 while (new_capacity < count && new_capacity <= max_capacity / 2)
42 new_capacity *= 2;
43 if (new_capacity < count)
44 new_capacity = count;
46 if (!*elements)
47 new_elements = heap_alloc_zero(new_capacity * size);
48 else
49 new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size);
50 if (!new_elements)
51 return FALSE;
53 *elements = new_elements;
54 *capacity = new_capacity;
55 return TRUE;
58 struct uia_node_array {
59 HUIANODE *nodes;
60 int node_count;
61 SIZE_T node_arr_size;
64 static void clear_node_array(struct uia_node_array *nodes)
66 if (nodes->nodes)
68 int i;
70 for (i = 0; i < nodes->node_count; i++)
71 UiaNodeRelease(nodes->nodes[i]);
73 heap_free(nodes->nodes);
76 memset(nodes, 0, sizeof(*nodes));
79 static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE node)
81 if (!uia_array_reserve((void **)&out_nodes->nodes, &out_nodes->node_arr_size, out_nodes->node_count + 1,
82 sizeof(node)))
83 return E_OUTOFMEMORY;
85 IWineUiaNode_AddRef((IWineUiaNode *)node);
86 out_nodes->nodes[out_nodes->node_count] = node;
87 out_nodes->node_count++;
89 return S_OK;
92 static HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems)
94 LONG ubound;
95 HRESULT hr;
97 *lbound = *elems = 0;
98 hr = SafeArrayGetLBound(sa, dim, lbound);
99 if (FAILED(hr))
100 return hr;
102 hr = SafeArrayGetUBound(sa, dim, &ubound);
103 if (FAILED(hr))
104 return hr;
106 *elems = (ubound - (*lbound)) + 1;
107 return S_OK;
110 static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems)
112 UINT dims;
114 *lbound = *elems = 0;
115 dims = SafeArrayGetDim(sa);
116 if (dims != 1)
118 WARN("Invalid dimensions %d for safearray.\n", dims);
119 return E_FAIL;
122 return get_safearray_dim_bounds(sa, 1, lbound, elems);
125 int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type)
127 LONG i, idx, lbound[2], elems[2];
128 int val[2];
129 HRESULT hr;
131 hr = get_safearray_bounds(sa1, &lbound[0], &elems[0]);
132 if (FAILED(hr))
134 ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr);
135 return -1;
138 hr = get_safearray_bounds(sa2, &lbound[1], &elems[1]);
139 if (FAILED(hr))
141 ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr);
142 return -1;
145 if (elems[0] != elems[1])
146 return (elems[0] > elems[1]) - (elems[0] < elems[1]);
148 if (prop_type != UIAutomationType_IntArray)
150 FIXME("Array type %#x value comparsion currently unimplemented.\n", prop_type);
151 return -1;
154 for (i = 0; i < elems[0]; i++)
156 idx = lbound[0] + i;
157 hr = SafeArrayGetElement(sa1, &idx, &val[0]);
158 if (FAILED(hr))
160 ERR("Failed to get element from sa1 with hr %#lx\n", hr);
161 return -1;
164 idx = lbound[1] + i;
165 hr = SafeArrayGetElement(sa2, &idx, &val[1]);
166 if (FAILED(hr))
168 ERR("Failed to get element from sa2 with hr %#lx\n", hr);
169 return -1;
172 if (val[0] != val[1])
173 return (val[0] > val[1]) - (val[0] < val[1]);
176 return 0;
179 static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems)
181 HUIANODE node;
182 HRESULT hr;
183 LONG i;
185 for (i = 0; i < elems; i++)
187 hr = SafeArrayGetElement(sa, &i, &node);
188 if (FAILED(hr))
189 break;
190 UiaNodeRelease(node);
194 static void create_uia_node_safearray(VARIANT *in, VARIANT *out)
196 LONG i, idx, lbound, elems;
197 HUIANODE node;
198 SAFEARRAY *sa;
199 HRESULT hr;
201 if (FAILED(get_safearray_bounds(V_ARRAY(in), &lbound, &elems)))
202 return;
204 if (!(sa = SafeArrayCreateVector(VT_UINT_PTR, 0, elems)))
205 return;
207 for (i = 0; i < elems; i++)
209 IRawElementProviderSimple *elprov;
210 IUnknown *unk;
212 idx = lbound + i;
213 hr = SafeArrayGetElement(V_ARRAY(in), &idx, &unk);
214 if (FAILED(hr))
215 break;
217 hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov);
218 IUnknown_Release(unk);
219 if (FAILED(hr))
220 break;
222 hr = UiaNodeFromProvider(elprov, &node);
223 if (FAILED(hr))
224 break;
226 IRawElementProviderSimple_Release(elprov);
227 hr = SafeArrayPutElement(sa, &i, &node);
228 if (FAILED(hr))
229 break;
232 if (FAILED(hr))
234 clear_uia_node_ptr_safearray(sa, elems);
235 SafeArrayDestroy(sa);
236 return;
239 V_VT(out) = VT_UINT_PTR | VT_ARRAY;
240 V_ARRAY(out) = sa;
243 /* Convert a VT_UINT_PTR SAFEARRAY to VT_UNKNOWN. */
244 static void uia_node_ptr_to_unk_safearray(VARIANT *in)
246 SAFEARRAY *sa = NULL;
247 LONG ubound, i;
248 HUIANODE node;
249 HRESULT hr;
251 hr = SafeArrayGetUBound(V_ARRAY(in), 1, &ubound);
252 if (FAILED(hr))
253 goto exit;
255 if (!(sa = SafeArrayCreateVector(VT_UNKNOWN, 0, ubound + 1)))
257 hr = E_FAIL;
258 goto exit;
261 for (i = 0; i < (ubound + 1); i++)
263 hr = SafeArrayGetElement(V_ARRAY(in), &i, &node);
264 if (FAILED(hr))
265 break;
267 hr = SafeArrayPutElement(sa, &i, node);
268 if (FAILED(hr))
269 break;
271 UiaNodeRelease(node);
274 exit:
275 if (FAILED(hr))
277 clear_uia_node_ptr_safearray(V_ARRAY(in), ubound + 1);
278 if (sa)
279 SafeArrayDestroy(sa);
282 VariantClear(in);
283 if (SUCCEEDED(hr))
285 V_VT(in) = VT_UNKNOWN | VT_ARRAY;
286 V_ARRAY(in) = sa;
290 static HRESULT get_global_interface_table(IGlobalInterfaceTable **git)
292 HRESULT hr;
294 hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL,
295 CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git);
296 if (FAILED(hr))
297 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr);
299 return hr;
302 static HWND get_hwnd_from_provider(IRawElementProviderSimple *elprov)
304 IRawElementProviderSimple *host_prov;
305 HRESULT hr;
306 VARIANT v;
307 HWND hwnd;
309 hwnd = NULL;
310 VariantInit(&v);
311 hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &host_prov);
312 if (SUCCEEDED(hr) && host_prov)
314 hr = IRawElementProviderSimple_GetPropertyValue(host_prov, UIA_NativeWindowHandlePropertyId, &v);
315 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
316 hwnd = UlongToHandle(V_I4(&v));
318 VariantClear(&v);
319 IRawElementProviderSimple_Release(host_prov);
322 if (!IsWindow(hwnd))
324 hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_NativeWindowHandlePropertyId, &v);
325 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
326 hwnd = UlongToHandle(V_I4(&v));
327 VariantClear(&v);
330 return hwnd;
333 static IRawElementProviderSimple *get_provider_hwnd_fragment_root(IRawElementProviderSimple *elprov, HWND *hwnd)
335 IRawElementProviderFragmentRoot *elroot, *elroot2;
336 IRawElementProviderSimple *elprov2, *ret;
337 IRawElementProviderFragment *elfrag;
338 HRESULT hr;
339 int depth;
341 *hwnd = NULL;
343 hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
344 if (FAILED(hr) || !elfrag)
345 return NULL;
347 depth = 0;
348 ret = NULL;
349 elroot = elroot2 = NULL;
352 * Recursively walk up the fragment root chain until:
353 * We get a fragment root that has an HWND associated with it.
354 * We get a NULL fragment root.
355 * We get the same fragment root as the current fragment root.
356 * We've gone up the chain ten times.
358 while (depth < 10)
360 hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot);
361 IRawElementProviderFragment_Release(elfrag);
362 if (FAILED(hr) || !elroot || (elroot == elroot2))
363 break;
365 hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderSimple, (void **)&elprov2);
366 if (FAILED(hr) || !elprov2)
367 break;
369 *hwnd = get_hwnd_from_provider(elprov2);
370 if (IsWindow(*hwnd))
372 ret = elprov2;
373 break;
376 hr = IRawElementProviderSimple_QueryInterface(elprov2, &IID_IRawElementProviderFragment, (void **)&elfrag);
377 IRawElementProviderSimple_Release(elprov2);
378 if (FAILED(hr) || !elfrag)
379 break;
381 if (elroot2)
382 IRawElementProviderFragmentRoot_Release(elroot2);
383 elroot2 = elroot;
384 elroot = NULL;
385 depth++;
388 if (elroot)
389 IRawElementProviderFragmentRoot_Release(elroot);
390 if (elroot2)
391 IRawElementProviderFragmentRoot_Release(elroot2);
393 return ret;
396 int get_node_provider_type_at_idx(struct uia_node *node, int idx)
398 int i, prov_idx;
400 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
402 if (node->prov[i])
404 if (prov_idx == idx)
405 return i;
406 else
407 prov_idx++;
411 ERR("Node %p has no provider at idx %d\n", node, idx);
412 return 0;
415 static HRESULT get_prov_opts_from_node_provider(IWineUiaNode *node, int idx, int *out_opts)
417 IWineUiaProvider *prov;
418 HRESULT hr;
420 *out_opts = 0;
421 hr = IWineUiaNode_get_provider(node, idx, &prov);
422 if (FAILED(hr))
423 return hr;
425 hr = IWineUiaProvider_get_prov_opts(prov, out_opts);
426 IWineUiaProvider_Release(prov);
428 return hr;
431 static HRESULT get_has_parent_from_node_provider(IWineUiaNode *node, int idx, BOOL *out_val)
433 IWineUiaProvider *prov;
434 HRESULT hr;
436 *out_val = FALSE;
437 hr = IWineUiaNode_get_provider(node, idx, &prov);
438 if (FAILED(hr))
439 return hr;
441 hr = IWineUiaProvider_has_parent(prov, out_val);
442 IWineUiaProvider_Release(prov);
444 return hr;
447 static HRESULT get_navigate_from_node_provider(IWineUiaNode *node, int idx, int nav_dir, VARIANT *ret_val)
449 IWineUiaProvider *prov;
450 HRESULT hr;
452 VariantInit(ret_val);
453 hr = IWineUiaNode_get_provider(node, idx, &prov);
454 if (FAILED(hr))
455 return hr;
457 hr = IWineUiaProvider_navigate(prov, nav_dir, ret_val);
458 IWineUiaProvider_Release(prov);
460 return hr;
464 * IWineUiaNode interface.
466 static HRESULT WINAPI uia_node_QueryInterface(IWineUiaNode *iface, REFIID riid, void **ppv)
468 *ppv = NULL;
469 if (IsEqualIID(riid, &IID_IWineUiaNode) || IsEqualIID(riid, &IID_IUnknown))
470 *ppv = iface;
471 else
472 return E_NOINTERFACE;
474 IWineUiaNode_AddRef(iface);
475 return S_OK;
478 static ULONG WINAPI uia_node_AddRef(IWineUiaNode *iface)
480 struct uia_node *node = impl_from_IWineUiaNode(iface);
481 ULONG ref = InterlockedIncrement(&node->ref);
483 TRACE("%p, refcount %ld\n", node, ref);
484 return ref;
487 static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
489 struct uia_node *node = impl_from_IWineUiaNode(iface);
490 ULONG ref = InterlockedDecrement(&node->ref);
492 TRACE("%p, refcount %ld\n", node, ref);
493 if (!ref)
495 int i;
497 for (i = 0; i < PROV_TYPE_COUNT; i++)
499 if (node->git_cookie[i])
501 IGlobalInterfaceTable *git;
502 HRESULT hr;
504 hr = get_global_interface_table(&git);
505 if (SUCCEEDED(hr))
507 hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[i]);
508 if (FAILED(hr))
509 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
513 if (node->prov[i])
514 IWineUiaProvider_Release(node->prov[i]);
517 if (!list_empty(&node->prov_thread_list_entry))
518 uia_provider_thread_remove_node((HUIANODE)iface);
519 if (node->nested_node)
520 uia_stop_provider_thread();
522 heap_free(node);
525 return ref;
528 static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, int idx, IWineUiaProvider **out_prov)
530 struct uia_node *node = impl_from_IWineUiaNode(iface);
531 int prov_type;
533 TRACE("(%p, %d, %p)\n", iface, idx, out_prov);
535 *out_prov = NULL;
536 if (node->disconnected)
537 return UIA_E_ELEMENTNOTAVAILABLE;
539 if (idx >= node->prov_count)
540 return E_INVALIDARG;
542 prov_type = get_node_provider_type_at_idx(node, idx);
543 if (node->git_cookie[prov_type])
545 IGlobalInterfaceTable *git;
546 IWineUiaProvider *prov;
547 HRESULT hr;
549 hr = get_global_interface_table(&git);
550 if (FAILED(hr))
551 return hr;
553 hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie[prov_type],
554 &IID_IWineUiaProvider, (void **)&prov);
555 if (FAILED(hr))
557 ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr);
558 return hr;
560 *out_prov = prov;
562 else
564 *out_prov = node->prov[prov_type];
565 IWineUiaProvider_AddRef(node->prov[prov_type]);
568 return S_OK;
571 static HRESULT WINAPI uia_node_get_prop_val(IWineUiaNode *iface, const GUID *prop_guid,
572 VARIANT *ret_val)
574 int prop_id = UiaLookupId(AutomationIdentifierType_Property, prop_guid);
575 struct uia_node *node = impl_from_IWineUiaNode(iface);
576 HRESULT hr;
577 VARIANT v;
579 TRACE("%p, %s, %p\n", iface, debugstr_guid(prop_guid), ret_val);
581 if (node->disconnected)
583 VariantInit(ret_val);
584 return UIA_E_ELEMENTNOTAVAILABLE;
587 hr = UiaGetPropertyValue((HUIANODE)iface, prop_id, &v);
589 /* VT_UNKNOWN is UiaGetReservedNotSupported value, no need to marshal it. */
590 if (V_VT(&v) == VT_UNKNOWN)
591 V_VT(ret_val) = VT_EMPTY;
592 else
593 *ret_val = v;
595 return hr;
598 static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface)
600 struct uia_node *node = impl_from_IWineUiaNode(iface);
601 int prov_type;
603 TRACE("%p\n", node);
605 if (node->disconnected)
607 ERR("Attempted to disconnect node which was already disconnected.\n");
608 return E_FAIL;
611 /* Nested nodes can only have one provider. */
612 prov_type = get_node_provider_type_at_idx(node, 0);
613 if (node->git_cookie[prov_type])
615 IGlobalInterfaceTable *git;
616 HRESULT hr;
618 hr = get_global_interface_table(&git);
619 if (SUCCEEDED(hr))
621 hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[prov_type]);
622 if (FAILED(hr))
623 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
625 node->git_cookie[prov_type] = 0;
628 IWineUiaProvider_Release(node->prov[prov_type]);
629 node->prov[prov_type] = NULL;
631 node->disconnected = TRUE;
632 node->prov_count = 0;
634 return S_OK;
637 static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd)
639 struct uia_node *node = impl_from_IWineUiaNode(iface);
641 TRACE("%p, %p\n", node, out_hwnd);
643 *out_hwnd = HandleToUlong(node->hwnd);
645 return S_OK;
648 static const IWineUiaNodeVtbl uia_node_vtbl = {
649 uia_node_QueryInterface,
650 uia_node_AddRef,
651 uia_node_Release,
652 uia_node_get_provider,
653 uia_node_get_prop_val,
654 uia_node_disconnect,
655 uia_node_get_hwnd,
658 static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface)
660 if (!iface || (iface->lpVtbl != &uia_node_vtbl))
661 return NULL;
663 return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface);
666 static BOOL is_nested_node_provider(IWineUiaProvider *iface);
667 static HRESULT prepare_uia_node(struct uia_node *node)
669 int i, prov_idx;
670 HRESULT hr;
672 /* Get the provider index for the provider that created the node. */
673 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
675 if (i == node->creator_prov_type)
677 node->creator_prov_idx = prov_idx;
678 break;
680 else if (node->prov[i])
681 prov_idx++;
685 * HUIANODEs can only have one 'parent link' provider, which handles
686 * parent and sibling navigation for the entire HUIANODE. Each provider is
687 * queried for a parent in descending order, starting with the override
688 * provider. The first provider to have a valid parent is made parent
689 * link. If no providers return a valid parent, the provider at index 0
690 * is made parent link by default.
692 for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
694 BOOL has_parent;
696 if (!node->prov[i])
697 continue;
699 hr = get_has_parent_from_node_provider(&node->IWineUiaNode_iface, prov_idx, &has_parent);
700 if (SUCCEEDED(hr) && has_parent)
702 node->parent_link_idx = prov_idx;
703 break;
706 prov_idx++;
709 for (i = 0; i < PROV_TYPE_COUNT; i++)
711 enum ProviderOptions prov_opts;
712 IGlobalInterfaceTable *git;
713 struct uia_provider *prov;
714 HRESULT hr;
716 /* Only regular providers need to be queried for UseComThreading. */
717 if (!node->prov[i] || is_nested_node_provider(node->prov[i]))
718 continue;
720 prov = impl_from_IWineUiaProvider(node->prov[i]);
721 hr = IRawElementProviderSimple_get_ProviderOptions(prov->elprov, &prov_opts);
722 if (FAILED(hr))
723 continue;
726 * If the UseComThreading ProviderOption is specified, all calls to the
727 * provided IRawElementProviderSimple need to respect the apartment type
728 * of the thread that creates the HUIANODE. i.e, if it's created in an
729 * STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
731 if (prov_opts & ProviderOptions_UseComThreading)
733 hr = get_global_interface_table(&git);
734 if (FAILED(hr))
735 return hr;
737 hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
738 &IID_IWineUiaProvider, &node->git_cookie[i]);
739 if (FAILED(hr))
740 return hr;
744 return S_OK;
747 static BOOL node_creator_is_parent_link(struct uia_node *node)
749 if (node->creator_prov_idx == node->parent_link_idx)
750 return TRUE;
751 else
752 return FALSE;
755 static HRESULT get_sibling_from_node_provider(struct uia_node *node, int prov_idx, int nav_dir,
756 VARIANT *out_node)
758 HUIANODE tmp_node;
759 HRESULT hr;
760 VARIANT v;
762 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, prov_idx, nav_dir, &v);
763 if (FAILED(hr))
764 return hr;
766 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
767 if (FAILED(hr))
768 goto exit;
770 while (1)
772 struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)tmp_node);
775 * If our sibling provider is the parent link of it's HUIANODE, then
776 * it is a valid sibling of this node.
778 if (node_creator_is_parent_link(node_data))
779 break;
782 * If our sibling provider is not the parent link of it's HUIANODE, we
783 * need to try the next sibling.
785 hr = get_navigate_from_node_provider((IWineUiaNode *)tmp_node, node_data->creator_prov_idx, nav_dir, &v);
786 UiaNodeRelease(tmp_node);
787 if (FAILED(hr))
788 return hr;
790 tmp_node = NULL;
791 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
792 if (FAILED(hr))
793 break;
796 exit:
797 if (tmp_node)
798 *out_node = v;
800 return S_OK;
803 static HRESULT get_child_for_node(struct uia_node *node, int start_prov_idx, int nav_dir, VARIANT *out_node)
805 int prov_idx = start_prov_idx;
806 HUIANODE tmp_node = NULL;
807 HRESULT hr;
808 VARIANT v;
810 while ((prov_idx >= 0) && (prov_idx < node->prov_count))
812 struct uia_node *node_data;
814 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, prov_idx, nav_dir, &v);
815 if (FAILED(hr))
816 return hr;
818 if (nav_dir == NavigateDirection_FirstChild)
819 prov_idx--;
820 else
821 prov_idx++;
823 /* If we failed to get a child, try the next provider. */
824 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
825 if (FAILED(hr))
826 continue;
828 node_data = impl_from_IWineUiaNode((IWineUiaNode *)tmp_node);
830 /* This child is the parent link of its node, so we can return it. */
831 if (node_creator_is_parent_link(node_data))
832 break;
835 * If the first child provider isn't the parent link of it's HUIANODE,
836 * we need to check the child provider for any valid siblings.
838 if (nav_dir == NavigateDirection_FirstChild)
839 hr = get_sibling_from_node_provider(node_data, node_data->creator_prov_idx,
840 NavigateDirection_NextSibling, &v);
841 else
842 hr = get_sibling_from_node_provider(node_data, node_data->creator_prov_idx,
843 NavigateDirection_PreviousSibling, &v);
845 UiaNodeRelease(tmp_node);
846 if (FAILED(hr))
847 return hr;
849 /* If we got a valid sibling from the child provider, return it. */
850 hr = UiaHUiaNodeFromVariant(&v, &tmp_node);
851 if (SUCCEEDED(hr))
852 break;
855 if (tmp_node)
856 *out_node = v;
858 return S_OK;
861 static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node)
863 HRESULT hr;
864 VARIANT v;
866 *out_node = NULL;
868 VariantInit(&v);
869 switch (nav_dir)
871 case NavigateDirection_FirstChild:
872 case NavigateDirection_LastChild:
873 /* First child always comes from last provider index. */
874 if (nav_dir == NavigateDirection_FirstChild)
875 hr = get_child_for_node(node, node->prov_count - 1, nav_dir, &v);
876 else
877 hr = get_child_for_node(node, 0, nav_dir, &v);
878 if (FAILED(hr))
879 WARN("Child navigation failed with hr %#lx\n", hr);
880 break;
882 case NavigateDirection_NextSibling:
883 case NavigateDirection_PreviousSibling:
885 struct uia_node *node_data;
886 HUIANODE parent;
887 VARIANT tmp;
889 hr = get_sibling_from_node_provider(node, node->parent_link_idx, nav_dir, &v);
890 if (FAILED(hr))
892 WARN("Sibling navigation failed with hr %#lx\n", hr);
893 break;
896 if (V_VT(&v) != VT_EMPTY)
897 break;
899 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, node->parent_link_idx,
900 NavigateDirection_Parent, &tmp);
901 if (FAILED(hr))
903 WARN("Parent navigation failed with hr %#lx\n", hr);
904 break;
907 hr = UiaHUiaNodeFromVariant(&tmp, &parent);
908 if (FAILED(hr))
909 break;
912 * If the parent node has multiple providers, attempt to get a sibling
913 * from one of them.
915 node_data = impl_from_IWineUiaNode((IWineUiaNode *)parent);
916 if (node_data->prov_count > 1)
918 if (nav_dir == NavigateDirection_NextSibling)
919 hr = get_child_for_node(node_data, node_data->creator_prov_idx - 1, NavigateDirection_FirstChild, &v);
920 else
921 hr = get_child_for_node(node_data, node_data->creator_prov_idx + 1, NavigateDirection_LastChild, &v);
924 UiaNodeRelease(parent);
925 break;
928 case NavigateDirection_Parent:
929 hr = get_navigate_from_node_provider(&node->IWineUiaNode_iface, node->parent_link_idx, nav_dir, &v);
930 if (FAILED(hr))
931 WARN("Parent navigation failed with hr %#lx\n", hr);
932 break;
934 default:
935 WARN("Invalid NavigateDirection %d\n", nav_dir);
936 return E_INVALIDARG;
939 if (V_VT(&v) != VT_EMPTY)
941 hr = UiaHUiaNodeFromVariant(&v, (HUIANODE *)out_node);
942 if (FAILED(hr))
943 WARN("UiaHUiaNodeFromVariant failed with hr %#lx\n", hr);
946 return S_OK;
949 static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition);
950 static BOOL uia_condition_matched(HRESULT hr);
953 * Assuming we have a tree that looks like this:
954 * +-------+
955 * | |
956 * | 1 |
957 * | |
958 * +---+---+
960 * +-----------+-----------+
961 * | | |
962 * +---+---+ +---+---+ +---+---+
963 * | | | | | |
964 * | 2 +---| 3 +---+ 4 |
965 * | | | | | |
966 * +---+---+ +-------+ +-------+
968 * +---+---+
969 * | |
970 * | 5 |
971 * | |
972 * +-------+
973 * If we start navigation of the tree from node 1, our visit order for a
974 * depth first search would be 1 -> 2 -> 5 -> 3 -> 4.
976 * However, if we start from the middle of the sequence at node 5 without the
977 * prior context of navigating from nodes 1 and 2, we need to use the function
978 * below to reach node 3, so we can visit the nodes within the tree in the
979 * same order of 5 -> 3 -> 4.
981 static HRESULT traverse_uia_node_tree_siblings(HUIANODE huianode, struct UiaCondition *ascending_stop_cond,
982 int dir, BOOL at_root_level, HUIANODE *out_node)
984 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
985 HUIANODE node2 = NULL;
986 HRESULT hr;
988 *out_node = NULL;
990 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
991 while (1)
993 hr = navigate_uia_node(node, dir, &node2);
994 if (FAILED(hr) || node2 || !at_root_level)
995 break;
997 hr = navigate_uia_node(node, NavigateDirection_Parent, &node2);
998 if (FAILED(hr) || !node2)
999 break;
1001 hr = uia_condition_check(node2, ascending_stop_cond);
1002 if (FAILED(hr) || uia_condition_matched(hr))
1004 UiaNodeRelease(node2);
1005 node2 = NULL;
1006 break;
1009 IWineUiaNode_Release(&node->IWineUiaNode_iface);
1010 node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node2);
1013 IWineUiaNode_Release(&node->IWineUiaNode_iface);
1014 *out_node = node2;
1016 return hr;
1020 * This function is used to traverse the 'raw' tree of HUIANODEs using a
1021 * depth-first search. As each node in the tree is visited, it is checked
1022 * against two conditions.
1024 * The first is the view condition, which is a filter for what is considered to
1025 * be a valid node in the current tree 'view'. The view condition essentially
1026 * creates a sort of 'virtual' tree. UI Automation provides three default tree
1027 * views:
1028 * -The 'raw' view has the view condition as ConditionType_True, so the tree
1029 * is completely unfiltered. All nodes are valid.
1030 * -The 'control' view contains only nodes that do not return VARIANT_FALSE
1031 * for UIA_IsControlElementPropertyId.
1032 * -The 'content' view contains only nodes that do not return VARIANT_FALSE
1033 * for both UIA_IsControlElementPropertyId and UIA_IsContentElementPropertyId.
1035 * If the currently visited node is considered to be valid within our view
1036 * condition, it is then checked against the search condition to determine if
1037 * it should be returned.
1039 static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond,
1040 struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond,
1041 struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL find_first,
1042 BOOL *root_found, int max_depth, int *cur_depth, struct uia_node_array *out_nodes)
1044 HUIANODE node = huianode;
1045 HRESULT hr;
1047 while (1)
1049 struct uia_node *node_data = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)node);
1050 BOOL incr_depth = FALSE;
1051 HUIANODE node2 = NULL;
1053 hr = uia_condition_check(node, view_cond);
1054 if (FAILED(hr))
1055 break;
1057 if (uia_condition_matched(hr))
1060 * If this is a valid node within our treeview, we need to increment
1061 * our current depth within the tree.
1063 incr_depth = TRUE;
1065 if (*root_found)
1067 hr = uia_condition_check(node, search_cond);
1068 if (FAILED(hr))
1069 break;
1071 if (uia_condition_matched(hr))
1073 hr = add_node_to_node_array(out_nodes, node);
1074 if (FAILED(hr))
1075 break;
1077 if (find_first)
1079 hr = S_FALSE;
1080 break;
1085 *root_found = TRUE;
1088 if (incr_depth)
1089 (*cur_depth)++;
1091 /* If we haven't exceeded our maximum traversal depth, visit children of this node. */
1092 if (max_depth < 0 || (*cur_depth) <= max_depth)
1094 if (traversal_opts & TreeTraversalOptions_LastToFirstOrder)
1095 hr = navigate_uia_node(node_data, NavigateDirection_LastChild, &node2);
1096 else
1097 hr = navigate_uia_node(node_data, NavigateDirection_FirstChild, &node2);
1099 if (SUCCEEDED(hr) && node2)
1100 hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond,
1101 traversal_opts, FALSE, find_first, root_found, max_depth, cur_depth, out_nodes);
1103 if (FAILED(hr) || hr == S_FALSE)
1104 break;
1107 if (incr_depth)
1108 (*cur_depth)--;
1111 * Before attempting to visit any siblings of huianode, make sure
1112 * huianode doesn't match our stop condition.
1114 hr = uia_condition_check(node, pre_sibling_nav_stop_cond);
1115 if (FAILED(hr) || uia_condition_matched(hr))
1116 break;
1118 /* Now, check for any siblings to visit. */
1119 if (traversal_opts & TreeTraversalOptions_LastToFirstOrder)
1120 hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_PreviousSibling,
1121 at_root_level, &node2);
1122 else
1123 hr = traverse_uia_node_tree_siblings(node, ascending_stop_cond, NavigateDirection_NextSibling,
1124 at_root_level, &node2);
1126 if (FAILED(hr) || !node2)
1127 break;
1129 UiaNodeRelease(node);
1130 node = node2;
1133 UiaNodeRelease(node);
1135 return hr;
1139 * IWineUiaProvider interface.
1141 static HRESULT WINAPI uia_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv)
1143 *ppv = NULL;
1144 if (IsEqualIID(riid, &IID_IWineUiaProvider) || IsEqualIID(riid, &IID_IUnknown))
1145 *ppv = iface;
1146 else
1147 return E_NOINTERFACE;
1149 IWineUiaProvider_AddRef(iface);
1150 return S_OK;
1153 static ULONG WINAPI uia_provider_AddRef(IWineUiaProvider *iface)
1155 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1156 ULONG ref = InterlockedIncrement(&prov->ref);
1158 TRACE("%p, refcount %ld\n", prov, ref);
1159 return ref;
1162 static void uia_stop_client_thread(void);
1163 static ULONG WINAPI uia_provider_Release(IWineUiaProvider *iface)
1165 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1166 ULONG ref = InterlockedDecrement(&prov->ref);
1168 TRACE("%p, refcount %ld\n", prov, ref);
1169 if (!ref)
1171 IRawElementProviderSimple_Release(prov->elprov);
1172 heap_free(prov);
1175 return ref;
1178 static void get_variant_for_node(HUIANODE node, VARIANT *v)
1180 #ifdef _WIN64
1181 V_VT(v) = VT_I8;
1182 V_I8(v) = (UINT64)node;
1183 #else
1184 V_VT(v) = VT_I4;
1185 V_I4(v) = (UINT32)node;
1186 #endif
1189 static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BOOL out_nested,
1190 VARIANT *v)
1192 HUIANODE node;
1193 HRESULT hr;
1195 VariantInit(v);
1196 hr = create_uia_node_from_elprov(elprov, &node, !out_nested);
1197 IRawElementProviderSimple_Release(elprov);
1198 if (SUCCEEDED(hr))
1200 if (out_nested)
1202 LRESULT lr = uia_lresult_from_node(node);
1204 if (!lr)
1205 return E_FAIL;
1207 V_VT(v) = VT_I4;
1208 V_I4(v) = lr;
1210 else
1211 get_variant_for_node(node, v);
1214 return S_OK;
1217 static HRESULT uia_provider_get_elem_prop_val(struct uia_provider *prov,
1218 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1220 HRESULT hr;
1221 VARIANT v;
1223 VariantInit(&v);
1224 hr = IRawElementProviderSimple_GetPropertyValue(prov->elprov, prop_info->prop_id, &v);
1225 if (FAILED(hr))
1226 goto exit;
1228 switch (prop_info->type)
1230 case UIAutomationType_Int:
1231 if (V_VT(&v) != VT_I4)
1233 WARN("Invalid vt %d for UIAutomationType_Int\n", V_VT(&v));
1234 goto exit;
1236 *ret_val = v;
1237 break;
1239 case UIAutomationType_IntArray:
1240 if (V_VT(&v) != (VT_I4 | VT_ARRAY))
1242 WARN("Invalid vt %d for UIAutomationType_IntArray\n", V_VT(&v));
1243 goto exit;
1245 *ret_val = v;
1246 break;
1248 case UIAutomationType_Double:
1249 if (V_VT(&v) != VT_R8)
1251 WARN("Invalid vt %d for UIAutomationType_Double\n", V_VT(&v));
1252 goto exit;
1254 *ret_val = v;
1255 break;
1257 case UIAutomationType_DoubleArray:
1258 if (V_VT(&v) != (VT_R8 | VT_ARRAY))
1260 WARN("Invalid vt %d for UIAutomationType_DoubleArray\n", V_VT(&v));
1261 goto exit;
1263 *ret_val = v;
1264 break;
1266 case UIAutomationType_Bool:
1267 if (V_VT(&v) != VT_BOOL)
1269 WARN("Invalid vt %d for UIAutomationType_Bool\n", V_VT(&v));
1270 goto exit;
1272 *ret_val = v;
1273 break;
1275 case UIAutomationType_String:
1276 if (V_VT(&v) != VT_BSTR)
1278 WARN("Invalid vt %d for UIAutomationType_String\n", V_VT(&v));
1279 goto exit;
1281 *ret_val = v;
1282 break;
1284 case UIAutomationType_Element:
1286 IRawElementProviderSimple *elprov;
1288 if (V_VT(&v) != VT_UNKNOWN)
1290 WARN("Invalid vt %d for UIAutomationType_Element\n", V_VT(&v));
1291 goto exit;
1294 hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IRawElementProviderSimple,
1295 (void **)&elprov);
1296 VariantClear(&v);
1297 if (FAILED(hr))
1298 goto exit;
1300 hr = get_variant_for_elprov_node(elprov, prov->return_nested_node, ret_val);
1301 if (FAILED(hr))
1302 return hr;
1304 break;
1307 case UIAutomationType_ElementArray:
1308 if (V_VT(&v) != (VT_UNKNOWN | VT_ARRAY))
1310 WARN("Invalid vt %d for UIAutomationType_ElementArray\n", V_VT(&v));
1311 goto exit;
1313 create_uia_node_safearray(&v, ret_val);
1314 if (V_VT(ret_val) == (VT_UINT_PTR | VT_ARRAY))
1315 VariantClear(&v);
1316 break;
1318 default:
1319 break;
1322 exit:
1323 if (V_VT(ret_val) == VT_EMPTY)
1324 VariantClear(&v);
1326 return S_OK;
1329 static SAFEARRAY *append_uia_runtime_id(SAFEARRAY *sa, HWND hwnd, enum ProviderOptions root_opts);
1330 static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov,
1331 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1333 HRESULT hr;
1335 switch (prop_info->prop_id)
1337 case UIA_RuntimeIdPropertyId:
1339 IRawElementProviderFragment *elfrag;
1340 SAFEARRAY *sa;
1341 LONG lbound;
1342 int val;
1344 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1345 if (FAILED(hr) || !elfrag)
1346 break;
1348 hr = IRawElementProviderFragment_GetRuntimeId(elfrag, &sa);
1349 IRawElementProviderFragment_Release(elfrag);
1350 if (FAILED(hr) || !sa)
1351 break;
1353 hr = SafeArrayGetLBound(sa, 1, &lbound);
1354 if (FAILED(hr))
1356 SafeArrayDestroy(sa);
1357 break;
1360 hr = SafeArrayGetElement(sa, &lbound, &val);
1361 if (FAILED(hr))
1363 SafeArrayDestroy(sa);
1364 break;
1367 if (val == UiaAppendRuntimeId)
1369 enum ProviderOptions prov_opts = 0;
1370 IRawElementProviderSimple *elprov;
1371 HWND hwnd;
1373 elprov = get_provider_hwnd_fragment_root(prov->elprov, &hwnd);
1374 if (!elprov)
1376 SafeArrayDestroy(sa);
1377 return E_FAIL;
1380 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1381 IRawElementProviderSimple_Release(elprov);
1382 if (FAILED(hr))
1383 WARN("get_ProviderOptions for root provider failed with %#lx\n", hr);
1385 if (!(sa = append_uia_runtime_id(sa, hwnd, prov_opts)))
1386 break;
1389 V_VT(ret_val) = VT_I4 | VT_ARRAY;
1390 V_ARRAY(ret_val) = sa;
1391 break;
1394 default:
1395 break;
1398 return S_OK;
1401 static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface,
1402 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1404 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1406 TRACE("%p, %p, %p\n", iface, prop_info, ret_val);
1408 VariantInit(ret_val);
1409 switch (prop_info->prop_type)
1411 case PROP_TYPE_ELEM_PROP:
1412 return uia_provider_get_elem_prop_val(prov, prop_info, ret_val);
1414 case PROP_TYPE_SPECIAL:
1415 return uia_provider_get_special_prop_val(prov, prop_info, ret_val);
1417 default:
1418 break;
1421 return S_OK;
1424 static HRESULT WINAPI uia_provider_get_prov_opts(IWineUiaProvider *iface, int *out_opts)
1426 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1427 enum ProviderOptions prov_opts;
1428 HRESULT hr;
1430 TRACE("%p, %p\n", iface, out_opts);
1432 *out_opts = 0;
1433 hr = IRawElementProviderSimple_get_ProviderOptions(prov->elprov, &prov_opts);
1434 if (SUCCEEDED(hr))
1435 *out_opts = prov_opts;
1437 return S_OK;
1440 static HRESULT WINAPI uia_provider_has_parent(IWineUiaProvider *iface, BOOL *out_val)
1442 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1444 TRACE("%p, %p\n", iface, out_val);
1446 if (!prov->parent_check_ran)
1448 IRawElementProviderFragment *elfrag, *elfrag2;
1449 HRESULT hr;
1451 prov->has_parent = FALSE;
1452 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1453 if (SUCCEEDED(hr) && elfrag)
1455 hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_Parent, &elfrag2);
1456 IRawElementProviderFragment_Release(elfrag);
1457 if (SUCCEEDED(hr) && elfrag2)
1459 prov->has_parent = TRUE;
1460 IRawElementProviderFragment_Release(elfrag2);
1464 prov->parent_check_ran = TRUE;
1467 *out_val = prov->has_parent;
1469 return S_OK;
1472 static HRESULT WINAPI uia_provider_navigate(IWineUiaProvider *iface, int nav_dir, VARIANT *out_val)
1474 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
1475 IRawElementProviderFragment *elfrag, *elfrag2;
1476 HRESULT hr;
1478 TRACE("%p, %d, %p\n", iface, nav_dir, out_val);
1480 VariantInit(out_val);
1481 hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
1482 if (FAILED(hr) || !elfrag)
1483 return S_OK;
1485 hr = IRawElementProviderFragment_Navigate(elfrag, nav_dir, &elfrag2);
1486 IRawElementProviderFragment_Release(elfrag);
1487 if (SUCCEEDED(hr) && elfrag2)
1489 IRawElementProviderSimple *elprov;
1491 hr = IRawElementProviderFragment_QueryInterface(elfrag2, &IID_IRawElementProviderSimple, (void **)&elprov);
1492 IRawElementProviderFragment_Release(elfrag2);
1493 if (FAILED(hr) || !elprov)
1494 return hr;
1496 hr = get_variant_for_elprov_node(elprov, prov->return_nested_node, out_val);
1497 if (FAILED(hr))
1498 return hr;
1501 return S_OK;
1504 static const IWineUiaProviderVtbl uia_provider_vtbl = {
1505 uia_provider_QueryInterface,
1506 uia_provider_AddRef,
1507 uia_provider_Release,
1508 uia_provider_get_prop_val,
1509 uia_provider_get_prov_opts,
1510 uia_provider_has_parent,
1511 uia_provider_navigate,
1514 static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov,
1515 int prov_type)
1517 struct uia_provider *prov = heap_alloc_zero(sizeof(*prov));
1519 if (!prov)
1520 return E_OUTOFMEMORY;
1522 prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
1523 prov->elprov = elprov;
1524 prov->ref = 1;
1525 node->prov[prov_type] = &prov->IWineUiaProvider_iface;
1526 if (!node->prov_count)
1527 node->creator_prov_type = prov_type;
1528 node->prov_count++;
1530 IRawElementProviderSimple_AddRef(elprov);
1531 return S_OK;
1534 static HRESULT uia_get_providers_for_hwnd(struct uia_node *node);
1535 HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node,
1536 BOOL get_hwnd_providers)
1538 static const int unsupported_prov_opts = ProviderOptions_ProviderOwnsSetFocus | ProviderOptions_HasNativeIAccessible |
1539 ProviderOptions_UseClientCoordinates;
1540 enum ProviderOptions prov_opts;
1541 struct uia_node *node;
1542 int prov_type;
1543 HRESULT hr;
1545 *out_node = NULL;
1547 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1548 if (FAILED(hr))
1549 return hr;
1551 if (prov_opts & unsupported_prov_opts)
1552 FIXME("Ignoring unsupported ProviderOption(s) %#x\n", prov_opts & unsupported_prov_opts);
1554 if (prov_opts & ProviderOptions_OverrideProvider)
1555 prov_type = PROV_TYPE_OVERRIDE;
1556 else if (prov_opts & ProviderOptions_NonClientAreaProvider)
1557 prov_type = PROV_TYPE_NONCLIENT;
1558 else if (prov_opts & ProviderOptions_ServerSideProvider)
1559 prov_type = PROV_TYPE_MAIN;
1560 else if (prov_opts & ProviderOptions_ClientSideProvider)
1561 prov_type = PROV_TYPE_HWND;
1562 else
1563 prov_type = PROV_TYPE_MAIN;
1565 node = heap_alloc_zero(sizeof(*node));
1566 if (!node)
1567 return E_OUTOFMEMORY;
1569 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
1570 node->hwnd = get_hwnd_from_provider(elprov);
1571 list_init(&node->prov_thread_list_entry);
1572 list_init(&node->node_map_list_entry);
1573 node->ref = 1;
1575 hr = create_wine_uia_provider(node, elprov, prov_type);
1576 if (FAILED(hr))
1578 heap_free(node);
1579 return hr;
1582 if (node->hwnd && get_hwnd_providers)
1584 hr = uia_get_providers_for_hwnd(node);
1585 if (FAILED(hr))
1586 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr);
1589 hr = prepare_uia_node(node);
1590 if (FAILED(hr))
1592 IWineUiaNode_Release(&node->IWineUiaNode_iface);
1593 return hr;
1596 *out_node = (void *)&node->IWineUiaNode_iface;
1598 return S_OK;
1601 /***********************************************************************
1602 * UiaNodeFromProvider (uiautomationcore.@)
1604 HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *huianode)
1606 TRACE("(%p, %p)\n", elprov, huianode);
1608 if (!elprov || !huianode)
1609 return E_INVALIDARG;
1611 return create_uia_node_from_elprov(elprov, huianode, TRUE);
1615 * UI Automation client thread functions.
1617 struct uia_client_thread
1619 CO_MTA_USAGE_COOKIE mta_cookie;
1620 HANDLE hthread;
1621 HWND hwnd;
1622 LONG ref;
1625 struct uia_get_node_prov_args {
1626 LRESULT lr;
1627 BOOL unwrap;
1630 static struct uia_client_thread client_thread;
1631 static CRITICAL_SECTION client_thread_cs;
1632 static CRITICAL_SECTION_DEBUG client_thread_cs_debug =
1634 0, 0, &client_thread_cs,
1635 { &client_thread_cs_debug.ProcessLocksList, &client_thread_cs_debug.ProcessLocksList },
1636 0, 0, { (DWORD_PTR)(__FILE__ ": client_thread_cs") }
1638 static CRITICAL_SECTION client_thread_cs = { &client_thread_cs_debug, -1, 0, 0, 0, 0 };
1640 #define WM_UIA_CLIENT_GET_NODE_PROV (WM_USER + 1)
1641 #define WM_UIA_CLIENT_THREAD_STOP (WM_USER + 2)
1642 static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, BOOL unwrap);
1643 static LRESULT CALLBACK uia_client_thread_msg_proc(HWND hwnd, UINT msg, WPARAM wparam,
1644 LPARAM lparam)
1646 switch (msg)
1648 case WM_UIA_CLIENT_GET_NODE_PROV:
1650 struct uia_get_node_prov_args *args = (struct uia_get_node_prov_args *)wparam;
1651 return create_wine_uia_nested_node_provider((struct uia_node *)lparam, args->lr, args->unwrap);
1654 default:
1655 break;
1658 return DefWindowProcW(hwnd, msg, wparam, lparam);
1661 static DWORD WINAPI uia_client_thread_proc(void *arg)
1663 HANDLE initialized_event = arg;
1664 HWND hwnd;
1665 MSG msg;
1667 hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
1668 if (!hwnd)
1670 WARN("CreateWindow failed: %ld\n", GetLastError());
1671 FreeLibraryAndExitThread(huia_module, 1);
1674 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)uia_client_thread_msg_proc);
1675 client_thread.hwnd = hwnd;
1677 /* Initialization complete, thread can now process window messages. */
1678 SetEvent(initialized_event);
1679 TRACE("Client thread started.\n");
1680 while (GetMessageW(&msg, NULL, 0, 0))
1682 if (msg.message == WM_UIA_CLIENT_THREAD_STOP)
1683 break;
1684 TranslateMessage(&msg);
1685 DispatchMessageW(&msg);
1688 TRACE("Shutting down UI Automation client thread.\n");
1690 DestroyWindow(hwnd);
1691 FreeLibraryAndExitThread(huia_module, 0);
1694 static BOOL uia_start_client_thread(void)
1696 BOOL started = TRUE;
1698 EnterCriticalSection(&client_thread_cs);
1699 if (++client_thread.ref == 1)
1701 HANDLE ready_event = NULL;
1702 HANDLE events[2];
1703 HMODULE hmodule;
1704 DWORD wait_obj;
1705 HRESULT hr;
1708 * We use CoIncrementMTAUsage here instead of CoInitialize because it
1709 * allows us to exit the implicit MTA immediately when the thread
1710 * reference count hits 0, rather than waiting for the thread to
1711 * shutdown and call CoUninitialize like the provider thread.
1713 hr = CoIncrementMTAUsage(&client_thread.mta_cookie);
1714 if (FAILED(hr))
1716 started = FALSE;
1717 goto exit;
1720 /* Increment DLL reference count. */
1721 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
1722 (const WCHAR *)uia_start_client_thread, &hmodule);
1724 events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1725 if (!(client_thread.hthread = CreateThread(NULL, 0, uia_client_thread_proc,
1726 ready_event, 0, NULL)))
1728 FreeLibrary(hmodule);
1729 started = FALSE;
1730 goto exit;
1733 events[1] = client_thread.hthread;
1734 wait_obj = WaitForMultipleObjects(2, events, FALSE, INFINITE);
1735 if (wait_obj != WAIT_OBJECT_0)
1737 CloseHandle(client_thread.hthread);
1738 started = FALSE;
1741 exit:
1742 if (ready_event)
1743 CloseHandle(ready_event);
1744 if (!started)
1746 WARN("Failed to start client thread\n");
1747 if (client_thread.mta_cookie)
1748 CoDecrementMTAUsage(client_thread.mta_cookie);
1749 memset(&client_thread, 0, sizeof(client_thread));
1753 LeaveCriticalSection(&client_thread_cs);
1754 return started;
1757 static void uia_stop_client_thread(void)
1759 EnterCriticalSection(&client_thread_cs);
1760 if (!--client_thread.ref)
1762 PostMessageW(client_thread.hwnd, WM_UIA_CLIENT_THREAD_STOP, 0, 0);
1763 CoDecrementMTAUsage(client_thread.mta_cookie);
1764 CloseHandle(client_thread.hthread);
1765 memset(&client_thread, 0, sizeof(client_thread));
1767 LeaveCriticalSection(&client_thread_cs);
1771 * IWineUiaProvider interface for nested node providers.
1773 * Nested node providers represent an HUIANODE that resides in another
1774 * thread or process. We retrieve values from this HUIANODE through the
1775 * IWineUiaNode interface.
1777 struct uia_nested_node_provider {
1778 IWineUiaProvider IWineUiaProvider_iface;
1779 LONG ref;
1781 IWineUiaNode *nested_node;
1784 static inline struct uia_nested_node_provider *impl_from_nested_node_IWineUiaProvider(IWineUiaProvider *iface)
1786 return CONTAINING_RECORD(iface, struct uia_nested_node_provider, IWineUiaProvider_iface);
1789 static HRESULT WINAPI uia_nested_node_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv)
1791 *ppv = NULL;
1792 if (IsEqualIID(riid, &IID_IWineUiaProvider) || IsEqualIID(riid, &IID_IUnknown))
1793 *ppv = iface;
1794 else
1795 return E_NOINTERFACE;
1797 IWineUiaProvider_AddRef(iface);
1798 return S_OK;
1801 static ULONG WINAPI uia_nested_node_provider_AddRef(IWineUiaProvider *iface)
1803 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1804 ULONG ref = InterlockedIncrement(&prov->ref);
1806 TRACE("%p, refcount %ld\n", prov, ref);
1807 return ref;
1810 static ULONG WINAPI uia_nested_node_provider_Release(IWineUiaProvider *iface)
1812 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1813 ULONG ref = InterlockedDecrement(&prov->ref);
1815 TRACE("%p, refcount %ld\n", prov, ref);
1816 if (!ref)
1818 IWineUiaNode_Release(prov->nested_node);
1819 uia_stop_client_thread();
1820 heap_free(prov);
1823 return ref;
1826 static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode);
1827 static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *iface,
1828 const struct uia_prop_info *prop_info, VARIANT *ret_val)
1830 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1831 HRESULT hr;
1832 VARIANT v;
1834 TRACE("%p, %p, %p\n", iface, prop_info, ret_val);
1836 VariantInit(ret_val);
1837 if (prop_info->type == UIAutomationType_ElementArray)
1839 FIXME("Element array property types currently unsupported for nested nodes.\n");
1840 return E_NOTIMPL;
1843 hr = IWineUiaNode_get_prop_val(prov->nested_node, prop_info->guid, &v);
1844 if (FAILED(hr))
1845 return hr;
1847 switch (prop_info->type)
1849 case UIAutomationType_Element:
1851 HUIANODE node;
1853 hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node);
1854 if (FAILED(hr))
1855 return hr;
1857 get_variant_for_node(node, ret_val);
1858 VariantClear(&v);
1859 break;
1862 default:
1863 *ret_val = v;
1864 break;
1867 return S_OK;
1870 static HRESULT WINAPI uia_nested_node_provider_get_prov_opts(IWineUiaProvider *iface, int *out_opts)
1872 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1874 TRACE("%p, %p\n", iface, out_opts);
1876 return get_prov_opts_from_node_provider(prov->nested_node, 0, out_opts);
1879 static HRESULT WINAPI uia_nested_node_provider_has_parent(IWineUiaProvider *iface, BOOL *out_val)
1881 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1883 TRACE("%p, %p\n", iface, out_val);
1885 return get_has_parent_from_node_provider(prov->nested_node, 0, out_val);
1888 static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, int nav_dir, VARIANT *out_val)
1890 struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
1891 HUIANODE node;
1892 HRESULT hr;
1893 VARIANT v;
1895 TRACE("%p, %d, %p\n", iface, nav_dir, out_val);
1897 VariantInit(out_val);
1898 hr = get_navigate_from_node_provider(prov->nested_node, 0, nav_dir, &v);
1899 if (FAILED(hr) || V_VT(&v) == VT_EMPTY)
1900 return hr;
1902 hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node);
1903 if (FAILED(hr))
1904 return hr;
1906 get_variant_for_node(node, out_val);
1907 VariantClear(&v);
1909 return S_OK;
1912 static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
1913 uia_nested_node_provider_QueryInterface,
1914 uia_nested_node_provider_AddRef,
1915 uia_nested_node_provider_Release,
1916 uia_nested_node_provider_get_prop_val,
1917 uia_nested_node_provider_get_prov_opts,
1918 uia_nested_node_provider_has_parent,
1919 uia_nested_node_provider_navigate,
1922 static BOOL is_nested_node_provider(IWineUiaProvider *iface)
1924 if (iface->lpVtbl == &uia_nested_node_provider_vtbl)
1925 return TRUE;
1927 return FALSE;
1930 static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr,
1931 BOOL unwrap)
1933 IWineUiaProvider *provider_iface = NULL;
1934 struct uia_nested_node_provider *prov;
1935 IGlobalInterfaceTable *git;
1936 IWineUiaNode *nested_node;
1937 int prov_opts, prov_type;
1938 DWORD git_cookie;
1939 HRESULT hr;
1941 hr = ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&nested_node);
1942 if (FAILED(hr))
1944 uia_stop_client_thread();
1945 return hr;
1948 hr = get_prov_opts_from_node_provider(nested_node, 0, &prov_opts);
1949 if (FAILED(hr))
1951 WARN("Failed to get provider options for node %p with hr %#lx\n", nested_node, hr);
1952 IWineUiaNode_Release(nested_node);
1953 uia_stop_client_thread();
1954 return hr;
1957 /* Nested nodes can only serve as override or main providers. */
1958 if (prov_opts & ProviderOptions_OverrideProvider)
1959 prov_type = PROV_TYPE_OVERRIDE;
1960 else
1961 prov_type = PROV_TYPE_MAIN;
1963 if (node->prov[prov_type])
1965 TRACE("Already have a provider of type %d for this node.\n", prov_type);
1966 IWineUiaNode_Release(nested_node);
1967 uia_stop_client_thread();
1968 return S_OK;
1972 * If we're retrieving a node from an HWND that belongs to the same thread
1973 * as the client making the request, return a normal provider instead of a
1974 * nested node provider.
1976 if (unwrap)
1978 struct uia_node *node_data = unsafe_impl_from_IWineUiaNode(nested_node);
1979 struct uia_provider *prov_data;
1981 if (!node_data)
1983 ERR("Failed to get uia_node structure from nested node\n");
1984 uia_stop_client_thread();
1985 return E_FAIL;
1988 provider_iface = node_data->prov[get_node_provider_type_at_idx(node_data, 0)];
1989 git_cookie = 0;
1991 IWineUiaProvider_AddRef(provider_iface);
1992 prov_data = impl_from_IWineUiaProvider(provider_iface);
1993 prov_data->return_nested_node = FALSE;
1994 prov_data->parent_check_ran = FALSE;
1996 IWineUiaNode_Release(nested_node);
1997 uia_stop_client_thread();
1999 else
2001 prov = heap_alloc_zero(sizeof(*prov));
2002 if (!prov)
2003 return E_OUTOFMEMORY;
2005 prov->IWineUiaProvider_iface.lpVtbl = &uia_nested_node_provider_vtbl;
2006 prov->nested_node = nested_node;
2007 prov->ref = 1;
2008 provider_iface = &prov->IWineUiaProvider_iface;
2011 * We need to use the GIT on all nested node providers so that our
2012 * IWineUiaNode proxy is used in the correct apartment.
2014 hr = get_global_interface_table(&git);
2015 if (FAILED(hr))
2017 IWineUiaProvider_Release(&prov->IWineUiaProvider_iface);
2018 return hr;
2021 hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
2022 &IID_IWineUiaProvider, &git_cookie);
2023 if (FAILED(hr))
2025 IWineUiaProvider_Release(&prov->IWineUiaProvider_iface);
2026 return hr;
2029 if (!node->hwnd)
2031 ULONG hwnd;
2033 hr = IWineUiaNode_get_hwnd(nested_node, &hwnd);
2034 if (SUCCEEDED(hr))
2035 node->hwnd = UlongToHandle(hwnd);
2039 node->prov[prov_type] = provider_iface;
2040 node->git_cookie[prov_type] = git_cookie;
2041 if (!node->prov_count)
2042 node->creator_prov_type = prov_type;
2043 node->prov_count++;
2045 return S_OK;
2048 static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode)
2050 struct uia_node *node;
2051 HRESULT hr;
2053 *huianode = NULL;
2054 node = heap_alloc_zero(sizeof(*node));
2055 if (!node)
2056 return E_OUTOFMEMORY;
2058 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
2059 list_init(&node->prov_thread_list_entry);
2060 list_init(&node->node_map_list_entry);
2061 node->ref = 1;
2063 uia_start_client_thread();
2064 hr = create_wine_uia_nested_node_provider(node, lr, FALSE);
2065 if (FAILED(hr))
2067 heap_free(node);
2068 return hr;
2071 if (node->hwnd)
2073 hr = uia_get_providers_for_hwnd(node);
2074 if (FAILED(hr))
2075 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr);
2078 hr = prepare_uia_node(node);
2079 if (FAILED(hr))
2081 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2082 return hr;
2085 *huianode = (void *)&node->IWineUiaNode_iface;
2087 return hr;
2091 * UiaNodeFromHandle is expected to work even if the calling thread hasn't
2092 * initialized COM. We marshal our node on a separate thread that initializes
2093 * COM for this reason.
2095 static HRESULT uia_get_provider_from_hwnd(struct uia_node *node)
2097 struct uia_get_node_prov_args args;
2099 if (!uia_start_client_thread())
2100 return E_FAIL;
2102 SetLastError(NOERROR);
2103 args.lr = SendMessageW(node->hwnd, WM_GETOBJECT, 0, UiaRootObjectId);
2104 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
2106 uia_stop_client_thread();
2107 return UIA_E_ELEMENTNOTAVAILABLE;
2110 if (!args.lr)
2112 uia_stop_client_thread();
2113 return S_FALSE;
2116 args.unwrap = GetCurrentThreadId() == GetWindowThreadProcessId(node->hwnd, NULL);
2117 return SendMessageW(client_thread.hwnd, WM_UIA_CLIENT_GET_NODE_PROV, (WPARAM)&args, (LPARAM)node);
2120 /***********************************************************************
2121 * UiaNodeFromHandle (uiautomationcore.@)
2123 HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode)
2125 struct uia_node *node;
2126 HRESULT hr;
2128 TRACE("(%p, %p)\n", hwnd, huianode);
2130 if (!huianode)
2131 return E_INVALIDARG;
2133 *huianode = NULL;
2135 if (!IsWindow(hwnd))
2136 return UIA_E_ELEMENTNOTAVAILABLE;
2138 node = heap_alloc_zero(sizeof(*node));
2139 if (!node)
2140 return E_OUTOFMEMORY;
2142 node->hwnd = hwnd;
2143 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
2144 list_init(&node->prov_thread_list_entry);
2145 list_init(&node->node_map_list_entry);
2146 node->ref = 1;
2148 hr = uia_get_providers_for_hwnd(node);
2149 if (FAILED(hr))
2151 heap_free(node);
2152 return hr;
2155 hr = prepare_uia_node(node);
2156 if (FAILED(hr))
2158 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2159 return hr;
2162 *huianode = (void *)&node->IWineUiaNode_iface;
2164 return S_OK;
2167 /***********************************************************************
2168 * UiaNodeRelease (uiautomationcore.@)
2170 BOOL WINAPI UiaNodeRelease(HUIANODE huianode)
2172 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2174 TRACE("(%p)\n", huianode);
2176 if (!node)
2177 return FALSE;
2179 IWineUiaNode_Release(&node->IWineUiaNode_iface);
2180 return TRUE;
2183 static HRESULT get_prop_val_from_node_provider(struct uia_node *node,
2184 const struct uia_prop_info *prop_info, VARIANT *v)
2186 IWineUiaProvider *prov;
2187 HRESULT hr = S_OK;
2188 int i;
2190 for (i = 0; i < node->prov_count; i++)
2192 hr = IWineUiaNode_get_provider(&node->IWineUiaNode_iface, i, &prov);
2193 if (FAILED(hr))
2194 return hr;
2196 VariantInit(v);
2197 hr = IWineUiaProvider_get_prop_val(prov, prop_info, v);
2198 IWineUiaProvider_Release(prov);
2199 if (FAILED(hr) || V_VT(v) != VT_EMPTY)
2200 break;
2203 return hr;
2206 /***********************************************************************
2207 * UiaGetPropertyValue (uiautomationcore.@)
2209 HRESULT WINAPI UiaGetPropertyValue(HUIANODE huianode, PROPERTYID prop_id, VARIANT *out_val)
2211 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2212 const struct uia_prop_info *prop_info;
2213 HRESULT hr;
2214 VARIANT v;
2216 TRACE("(%p, %d, %p)\n", huianode, prop_id, out_val);
2218 if (!node || !out_val)
2219 return E_INVALIDARG;
2221 V_VT(out_val) = VT_UNKNOWN;
2222 UiaGetReservedNotSupportedValue(&V_UNKNOWN(out_val));
2224 prop_info = uia_prop_info_from_id(prop_id);
2225 if (!prop_info)
2226 return E_INVALIDARG;
2228 if (!prop_info->type)
2230 FIXME("No type info for prop_id %d\n", prop_id);
2231 return E_NOTIMPL;
2234 switch (prop_id)
2236 case UIA_RuntimeIdPropertyId:
2238 SAFEARRAY *sa;
2240 hr = UiaGetRuntimeId(huianode, &sa);
2241 if (SUCCEEDED(hr) && sa)
2243 V_VT(out_val) = VT_I4 | VT_ARRAY;
2244 V_ARRAY(out_val) = sa;
2246 return S_OK;
2249 default:
2250 break;
2253 hr = get_prop_val_from_node_provider(node, prop_info, &v);
2254 if (SUCCEEDED(hr) && V_VT(&v) != VT_EMPTY)
2257 * ElementArray types come back as an array of pointers to prevent the
2258 * HUIANODEs from getting marshaled. We need to convert them to
2259 * VT_UNKNOWN here.
2261 if (prop_info->type == UIAutomationType_ElementArray)
2263 uia_node_ptr_to_unk_safearray(&v);
2264 if (V_VT(&v) != VT_EMPTY)
2265 *out_val = v;
2267 else
2268 *out_val = v;
2271 return hr;
2274 #define UIA_RUNTIME_ID_PREFIX 42
2276 enum fragment_root_prov_type_ids {
2277 FRAGMENT_ROOT_NONCLIENT_TYPE_ID = 0x03,
2278 FRAGMENT_ROOT_MAIN_TYPE_ID = 0x04,
2279 FRAGMENT_ROOT_OVERRIDE_TYPE_ID = 0x05,
2282 static HRESULT write_runtime_id_base(SAFEARRAY *sa, HWND hwnd)
2284 const int rt_id[2] = { UIA_RUNTIME_ID_PREFIX, HandleToUlong(hwnd) };
2285 HRESULT hr;
2286 LONG idx;
2288 for (idx = 0; idx < ARRAY_SIZE(rt_id); idx++)
2290 hr = SafeArrayPutElement(sa, &idx, (void *)&rt_id[idx]);
2291 if (FAILED(hr))
2292 return hr;
2295 return S_OK;
2298 static SAFEARRAY *append_uia_runtime_id(SAFEARRAY *sa, HWND hwnd, enum ProviderOptions root_opts)
2300 LONG i, idx, lbound, elems;
2301 SAFEARRAY *sa2, *ret;
2302 HRESULT hr;
2303 int val;
2305 ret = sa2 = NULL;
2306 hr = get_safearray_bounds(sa, &lbound, &elems);
2307 if (FAILED(hr))
2308 goto exit;
2310 /* elems includes the UiaAppendRuntimeId value, so we only add 2. */
2311 if (!(sa2 = SafeArrayCreateVector(VT_I4, 0, elems + 2)))
2312 goto exit;
2314 hr = write_runtime_id_base(sa2, hwnd);
2315 if (FAILED(hr))
2316 goto exit;
2318 if (root_opts & ProviderOptions_NonClientAreaProvider)
2319 val = FRAGMENT_ROOT_NONCLIENT_TYPE_ID;
2320 else if (root_opts & ProviderOptions_OverrideProvider)
2321 val = FRAGMENT_ROOT_OVERRIDE_TYPE_ID;
2322 else
2323 val = FRAGMENT_ROOT_MAIN_TYPE_ID;
2325 idx = 2;
2326 hr = SafeArrayPutElement(sa2, &idx, &val);
2327 if (FAILED(hr))
2328 goto exit;
2330 for (i = 0; i < (elems - 1); i++)
2332 idx = (lbound + 1) + i;
2333 hr = SafeArrayGetElement(sa, &idx, &val);
2334 if (FAILED(hr))
2335 goto exit;
2337 idx = (3 + i);
2338 hr = SafeArrayPutElement(sa2, &idx, &val);
2339 if (FAILED(hr))
2340 goto exit;
2343 ret = sa2;
2345 exit:
2347 if (!ret)
2348 SafeArrayDestroy(sa2);
2350 SafeArrayDestroy(sa);
2351 return ret;
2354 /***********************************************************************
2355 * UiaGetRuntimeId (uiautomationcore.@)
2357 HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id)
2359 const struct uia_prop_info *prop_info = uia_prop_info_from_id(UIA_RuntimeIdPropertyId);
2360 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2361 HRESULT hr;
2363 TRACE("(%p, %p)\n", huianode, runtime_id);
2365 if (!node || !runtime_id)
2366 return E_INVALIDARG;
2368 *runtime_id = NULL;
2370 /* Provide an HWND based runtime ID if the node has an HWND. */
2371 if (node->hwnd)
2373 SAFEARRAY *sa;
2375 if (!(sa = SafeArrayCreateVector(VT_I4, 0, 2)))
2376 return E_FAIL;
2378 hr = write_runtime_id_base(sa, node->hwnd);
2379 if (FAILED(hr))
2381 SafeArrayDestroy(sa);
2382 return hr;
2385 *runtime_id = sa;
2386 return S_OK;
2388 else
2390 VARIANT v;
2392 hr = get_prop_val_from_node_provider(node, prop_info, &v);
2393 if (FAILED(hr))
2395 VariantClear(&v);
2396 return hr;
2399 if (V_VT(&v) == (VT_I4 | VT_ARRAY))
2400 *runtime_id = V_ARRAY(&v);
2403 return S_OK;
2406 /***********************************************************************
2407 * UiaHUiaNodeFromVariant (uiautomationcore.@)
2409 HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode)
2411 const VARTYPE expected_vt = sizeof(void *) == 8 ? VT_I8 : VT_I4;
2413 TRACE("(%p, %p)\n", in_val, huianode);
2415 if (!in_val || !huianode)
2416 return E_INVALIDARG;
2418 *huianode = NULL;
2419 if ((V_VT(in_val) != expected_vt) && (V_VT(in_val) != VT_UNKNOWN))
2421 WARN("Invalid vt %d\n", V_VT(in_val));
2422 return E_INVALIDARG;
2425 if (V_VT(in_val) == VT_UNKNOWN)
2427 if (V_UNKNOWN(in_val))
2428 IUnknown_AddRef(V_UNKNOWN(in_val));
2429 *huianode = (HUIANODE)V_UNKNOWN(in_val);
2431 else
2433 #ifdef _WIN64
2434 *huianode = (HUIANODE)V_I8(in_val);
2435 #else
2436 *huianode = (HUIANODE)V_I4(in_val);
2437 #endif
2440 return S_OK;
2443 static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderType prov_type)
2445 switch (prov_type)
2447 case ProviderType_Proxy:
2448 FIXME("Default ProviderType_Proxy MSAA provider unimplemented.\n");
2449 break;
2451 case ProviderType_NonClientArea:
2452 FIXME("Default ProviderType_NonClientArea provider unimplemented.\n");
2453 break;
2455 case ProviderType_BaseHwnd:
2456 FIXME("Default ProviderType_BaseHwnd provider unimplemented.\n");
2457 break;
2459 default:
2460 break;
2463 return NULL;
2466 static UiaProviderCallback *uia_provider_callback = default_uia_provider_callback;
2468 static HRESULT uia_get_clientside_provider(struct uia_node *node, int prov_type,
2469 int node_prov_type)
2471 IRawElementProviderSimple *elprov;
2472 LONG lbound, elems;
2473 SAFEARRAY *sa;
2474 IUnknown *unk;
2475 VARTYPE vt;
2476 HRESULT hr;
2478 if (!(sa = uia_provider_callback(node->hwnd, prov_type)))
2479 return S_OK;
2481 hr = SafeArrayGetVartype(sa, &vt);
2482 if (FAILED(hr) || (vt != VT_UNKNOWN))
2483 goto exit;
2485 hr = get_safearray_bounds(sa, &lbound, &elems);
2486 if (FAILED(hr))
2487 goto exit;
2489 /* Returned SAFEARRAY can only have 1 element. */
2490 if (elems != 1)
2492 WARN("Invalid element count %ld for returned SAFEARRAY\n", elems);
2493 goto exit;
2496 hr = SafeArrayGetElement(sa, &lbound, &unk);
2497 if (FAILED(hr))
2498 goto exit;
2500 hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov);
2501 IUnknown_Release(unk);
2502 if (FAILED(hr) || !elprov)
2504 WARN("Failed to get IRawElementProviderSimple from returned SAFEARRAY.\n");
2505 hr = S_OK;
2506 goto exit;
2509 hr = create_wine_uia_provider(node, elprov, node_prov_type);
2510 IRawElementProviderSimple_Release(elprov);
2512 exit:
2513 if (FAILED(hr))
2514 WARN("Failed to get clientside provider, hr %#lx\n", hr);
2515 SafeArrayDestroy(sa);
2516 return hr;
2519 static HRESULT uia_get_providers_for_hwnd(struct uia_node *node)
2521 HRESULT hr;
2523 hr = uia_get_provider_from_hwnd(node);
2524 if (FAILED(hr))
2525 return hr;
2527 if (!node->prov[PROV_TYPE_MAIN])
2529 hr = uia_get_clientside_provider(node, ProviderType_Proxy, PROV_TYPE_MAIN);
2530 if (FAILED(hr))
2531 return hr;
2534 if (!node->prov[PROV_TYPE_OVERRIDE])
2535 FIXME("Override provider callback currently unimplemented.\n");
2537 if (!node->prov[PROV_TYPE_NONCLIENT])
2539 hr = uia_get_clientside_provider(node, ProviderType_NonClientArea, PROV_TYPE_NONCLIENT);
2540 if (FAILED(hr))
2541 return hr;
2544 if (!node->prov[PROV_TYPE_HWND])
2546 hr = uia_get_clientside_provider(node, ProviderType_BaseHwnd, PROV_TYPE_HWND);
2547 if (FAILED(hr))
2548 return hr;
2551 if (!node->prov_count)
2553 if (uia_provider_callback == default_uia_provider_callback)
2554 return E_NOTIMPL;
2555 else
2556 return E_FAIL;
2559 return S_OK;
2562 /***********************************************************************
2563 * UiaRegisterProviderCallback (uiautomationcore.@)
2565 void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback)
2567 TRACE("(%p)\n", callback);
2569 if (callback)
2570 uia_provider_callback = callback;
2571 else
2572 uia_provider_callback = default_uia_provider_callback;
2575 static BOOL uia_condition_matched(HRESULT hr)
2577 if (hr == S_FALSE)
2578 return FALSE;
2579 else
2580 return TRUE;
2583 static HRESULT uia_property_condition_check(HUIANODE node, struct UiaPropertyCondition *prop_cond)
2585 const struct uia_prop_info *prop_info = uia_prop_info_from_id(prop_cond->PropertyId);
2586 HRESULT hr;
2587 VARIANT v;
2589 if (!prop_info)
2590 return E_INVALIDARG;
2592 switch (prop_info->type)
2594 case UIAutomationType_Bool:
2595 case UIAutomationType_IntArray:
2596 break;
2598 default:
2599 FIXME("PropertyCondition comparison unimplemented for type %#x\n", prop_info->type);
2600 return E_NOTIMPL;
2603 hr = UiaGetPropertyValue(node, prop_info->prop_id, &v);
2604 if (FAILED(hr) || V_VT(&v) == VT_UNKNOWN)
2605 return S_FALSE;
2607 if (V_VT(&v) == V_VT(&prop_cond->Value))
2609 switch (prop_info->type)
2611 case UIAutomationType_Bool:
2612 if (V_BOOL(&v) == V_BOOL(&prop_cond->Value))
2613 hr = S_OK;
2614 else
2615 hr = S_FALSE;
2616 break;
2618 case UIAutomationType_IntArray:
2619 if (!uia_compare_safearrays(V_ARRAY(&v), V_ARRAY(&prop_cond->Value), prop_info->type))
2620 hr = S_OK;
2621 else
2622 hr = S_FALSE;
2623 break;
2625 default:
2626 break;
2629 else
2630 hr = S_FALSE;
2632 VariantClear(&v);
2633 return hr;
2636 static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition)
2638 HRESULT hr;
2640 switch (condition->ConditionType)
2642 case ConditionType_True:
2643 return S_OK;
2645 case ConditionType_False:
2646 return S_FALSE;
2648 case ConditionType_Not:
2650 struct UiaNotCondition *not_cond = (struct UiaNotCondition *)condition;
2652 hr = uia_condition_check(node, not_cond->pConditions);
2653 if (FAILED(hr))
2654 return hr;
2656 if (uia_condition_matched(hr))
2657 return S_FALSE;
2658 else
2659 return S_OK;
2662 case ConditionType_And:
2663 case ConditionType_Or:
2665 struct UiaAndOrCondition *and_or_cond = (struct UiaAndOrCondition *)condition;
2666 int i;
2668 for (i = 0; i < and_or_cond->cConditions; i++)
2670 hr = uia_condition_check(node, and_or_cond->ppConditions[i]);
2671 if (FAILED(hr))
2672 return hr;
2674 if (condition->ConditionType == ConditionType_And && !uia_condition_matched(hr))
2675 return S_FALSE;
2676 else if (condition->ConditionType == ConditionType_Or && uia_condition_matched(hr))
2677 return S_OK;
2680 if (condition->ConditionType == ConditionType_Or)
2681 return S_FALSE;
2682 else
2683 return S_OK;
2686 case ConditionType_Property:
2687 return uia_property_condition_check(node, (struct UiaPropertyCondition *)condition);
2689 default:
2690 WARN("Invalid condition type %d\n", condition->ConditionType);
2691 return E_INVALIDARG;
2695 /***********************************************************************
2696 * UiaGetUpdatedCache (uiautomationcore.@)
2698 HRESULT WINAPI UiaGetUpdatedCache(HUIANODE huianode, struct UiaCacheRequest *cache_req, enum NormalizeState normalize_state,
2699 struct UiaCondition *normalize_cond, SAFEARRAY **out_req, BSTR *tree_struct)
2701 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2702 struct UiaCondition *cond;
2703 SAFEARRAYBOUND sabound[2];
2704 SAFEARRAY *sa;
2705 LONG idx[2];
2706 HRESULT hr;
2707 VARIANT v;
2709 TRACE("(%p, %p, %u, %p, %p, %p)\n", huianode, cache_req, normalize_state, normalize_cond, out_req, tree_struct);
2711 if (!node || !out_req || !tree_struct || !cache_req)
2712 return E_INVALIDARG;
2714 *tree_struct = NULL;
2715 *out_req = NULL;
2717 if (cache_req->Scope != TreeScope_Element)
2719 FIXME("Unsupported cache request scope %#x\n", cache_req->Scope);
2720 return E_NOTIMPL;
2723 switch (normalize_state)
2725 case NormalizeState_None:
2726 cond = NULL;
2727 break;
2729 case NormalizeState_View:
2730 cond = cache_req->pViewCondition;
2731 break;
2733 case NormalizeState_Custom:
2734 cond = normalize_cond;
2735 break;
2737 default:
2738 WARN("Invalid normalize_state %d\n", normalize_state);
2739 return E_INVALIDARG;
2742 if (cond)
2744 hr = uia_condition_check(huianode, cond);
2745 if (FAILED(hr))
2746 return hr;
2748 if (!uia_condition_matched(hr))
2750 *tree_struct = SysAllocString(L"");
2751 return S_OK;
2755 sabound[0].cElements = sabound[1].cElements = 1;
2756 sabound[0].lLbound = sabound[1].lLbound = 0;
2757 if (!(sa = SafeArrayCreate(VT_VARIANT, 2, sabound)))
2759 WARN("Failed to create safearray\n");
2760 return E_FAIL;
2763 get_variant_for_node(huianode, &v);
2764 idx[0] = idx[1] = 0;
2766 hr = SafeArrayPutElement(sa, idx, &v);
2767 if (FAILED(hr))
2769 SafeArrayDestroy(sa);
2770 return hr;
2774 * AddRef huianode since we're returning a reference to the same node we
2775 * passed in, rather than creating a new one.
2777 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
2779 *out_req = sa;
2780 *tree_struct = SysAllocString(L"P)");
2782 return S_OK;
2785 /***********************************************************************
2786 * UiaNavigate (uiautomationcore.@)
2788 HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct UiaCondition *nav_condition,
2789 struct UiaCacheRequest *cache_req, SAFEARRAY **out_req, BSTR *tree_struct)
2791 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2792 HUIANODE node2;
2793 HRESULT hr;
2795 TRACE("(%p, %u, %p, %p, %p, %p)\n", huianode, dir, nav_condition, cache_req, out_req,
2796 tree_struct);
2798 if (!node || !nav_condition || !cache_req || !out_req || !tree_struct)
2799 return E_INVALIDARG;
2801 *out_req = NULL;
2802 *tree_struct = NULL;
2804 if (nav_condition->ConditionType != ConditionType_True)
2806 FIXME("ConditionType %d based navigation is not implemented.\n", nav_condition->ConditionType);
2807 return E_NOTIMPL;
2810 hr = navigate_uia_node(node, dir, &node2);
2811 if (FAILED(hr))
2812 return hr;
2814 if (node2)
2816 hr = UiaGetUpdatedCache(node2, cache_req, NormalizeState_None, NULL, out_req, tree_struct);
2817 if (FAILED(hr))
2818 WARN("UiaGetUpdatedCache failed with hr %#lx\n", hr);
2819 UiaNodeRelease(node2);
2822 return hr;
2825 /* Combine multiple cache requests into a single SAFEARRAY. */
2826 static HRESULT uia_cache_request_combine(SAFEARRAY **reqs, int reqs_count, SAFEARRAY *out_req)
2828 LONG idx[2], lbound[2], elems[2], cur_offset;
2829 int i, x, y;
2830 HRESULT hr;
2831 VARIANT v;
2833 for (i = cur_offset = 0; i < reqs_count; i++)
2835 if (!reqs[i])
2836 continue;
2838 for (x = 0; x < 2; x++)
2840 hr = get_safearray_dim_bounds(reqs[i], 1 + x, &lbound[x], &elems[x]);
2841 if (FAILED(hr))
2842 return hr;
2845 for (x = 0; x < elems[0]; x++)
2847 for (y = 0; y < elems[1]; y++)
2849 idx[0] = x + lbound[0];
2850 idx[1] = y + lbound[1];
2851 hr = SafeArrayGetElement(reqs[i], idx, &v);
2852 if (FAILED(hr))
2853 return hr;
2855 idx[0] = x + cur_offset;
2856 idx[1] = y;
2857 hr = SafeArrayPutElement(out_req, idx, &v);
2858 if (FAILED(hr))
2859 return hr;
2863 cur_offset += elems[0];
2866 return S_OK;
2869 /***********************************************************************
2870 * UiaFind (uiautomationcore.@)
2872 HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, struct UiaCacheRequest *cache_req,
2873 SAFEARRAY **out_req, SAFEARRAY **out_offsets, SAFEARRAY **out_tree_structs)
2875 struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId };
2876 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
2877 SAFEARRAY *runtime_id, *req, *offsets, *tree_structs, **tmp_reqs;
2878 struct UiaCondition *sibling_stop_cond;
2879 struct uia_node_array nodes = { 0 };
2880 LONG idx, lbound, elems, cur_offset;
2881 SAFEARRAYBOUND sabound[2];
2882 int i, cur_depth = 0;
2883 BSTR tree_struct;
2884 BOOL root_found;
2885 HRESULT hr;
2887 TRACE("(%p, %p, %p, %p, %p, %p)\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs);
2889 if (!node || !find_params || !cache_req || !out_req || !out_offsets || !out_tree_structs)
2890 return E_INVALIDARG;
2892 *out_tree_structs = *out_offsets = *out_req = tree_structs = offsets = req = NULL;
2893 tmp_reqs = NULL;
2896 * If the initial node has a runtime ID, we'll use it as a stop
2897 * condition.
2899 hr = UiaGetRuntimeId(huianode, &runtime_id);
2900 if (SUCCEEDED(hr) && runtime_id)
2902 V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY;
2903 V_ARRAY(&prop_cond.Value) = runtime_id;
2904 sibling_stop_cond = (struct UiaCondition *)&prop_cond;
2906 else
2907 sibling_stop_cond = (struct UiaCondition *)&UiaFalseCondition;
2909 if (find_params->ExcludeRoot)
2910 root_found = FALSE;
2911 else
2912 root_found = TRUE;
2914 IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
2915 hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond,
2916 cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->FindFirst, &root_found,
2917 find_params->MaxDepth, &cur_depth, &nodes);
2918 if (FAILED(hr) || !nodes.node_count)
2919 goto exit;
2921 if (!(offsets = SafeArrayCreateVector(VT_I4, 0, nodes.node_count)))
2923 hr = E_FAIL;
2924 goto exit;
2927 if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, nodes.node_count)))
2929 hr = E_FAIL;
2930 goto exit;
2933 if (!(tmp_reqs = heap_alloc_zero(sizeof(*tmp_reqs) * nodes.node_count)))
2935 hr = E_OUTOFMEMORY;
2936 goto exit;
2940 * Get a count of how many total nodes we'll need to return, as well as
2941 * set the tree structure strings and cache request offsets for our final
2942 * combined SAFEARRAY.
2944 for (i = cur_offset = 0; i < nodes.node_count; i++)
2946 hr = UiaGetUpdatedCache(nodes.nodes[i], cache_req, NormalizeState_None, NULL, &tmp_reqs[i], &tree_struct);
2947 if (FAILED(hr))
2948 goto exit;
2950 idx = i;
2951 hr = SafeArrayPutElement(tree_structs, &idx, tree_struct);
2952 SysFreeString(tree_struct);
2953 if (FAILED(hr))
2954 goto exit;
2956 hr = SafeArrayPutElement(offsets, &idx, &cur_offset);
2957 if (FAILED(hr))
2958 goto exit;
2960 if (!tmp_reqs[i])
2961 continue;
2963 hr = get_safearray_dim_bounds(tmp_reqs[i], 1, &lbound, &elems);
2964 if (FAILED(hr))
2965 goto exit;
2967 cur_offset += elems;
2970 if (nodes.node_count == 1)
2972 req = tmp_reqs[0];
2973 heap_free(tmp_reqs);
2974 tmp_reqs = NULL;
2976 else
2978 sabound[0].lLbound = sabound[1].lLbound = 0;
2979 sabound[0].cElements = cur_offset;
2980 sabound[1].cElements = 1 + cache_req->cProperties + cache_req->cPatterns;
2981 if (!(req = SafeArrayCreate(VT_VARIANT, 2, sabound)))
2983 hr = E_FAIL;
2984 goto exit;
2987 hr = uia_cache_request_combine(tmp_reqs, nodes.node_count, req);
2988 if (FAILED(hr))
2989 goto exit;
2992 *out_tree_structs = tree_structs;
2993 *out_offsets = offsets;
2994 *out_req = req;
2996 exit:
2997 VariantClear(&prop_cond.Value);
2998 clear_node_array(&nodes);
3000 if (tmp_reqs)
3002 for (i = 0; i < nodes.node_count; i++)
3003 SafeArrayDestroy(tmp_reqs[i]);
3004 heap_free(tmp_reqs);
3007 if (FAILED(hr))
3009 SafeArrayDestroy(tree_structs);
3010 SafeArrayDestroy(offsets);
3011 SafeArrayDestroy(req);
3014 return hr;
3017 /***********************************************************************
3018 * UiaAddEvent (uiautomationcore.@)
3020 HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope,
3021 PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent)
3023 FIXME("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count,
3024 cache_req, huiaevent);
3025 return E_NOTIMPL;
3028 /***********************************************************************
3029 * UiaRemoveEvent (uiautomationcore.@)
3031 HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
3033 FIXME("(%p): stub\n", huiaevent);
3034 return E_NOTIMPL;
3037 /***********************************************************************
3038 * UiaEventAddWindow (uiautomationcore.@)
3040 HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd)
3042 FIXME("(%p, %p): stub\n", huiaevent, hwnd);
3043 return E_NOTIMPL;
3046 /***********************************************************************
3047 * UiaEventRemoveWindow (uiautomationcore.@)
3049 HRESULT WINAPI UiaEventRemoveWindow(HUIAEVENT huiaevent, HWND hwnd)
3051 FIXME("(%p, %p): stub\n", huiaevent, hwnd);
3052 return E_NOTIMPL;