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
{
33 static void clear_node_array(struct uia_node_array
*nodes
)
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,
54 IWineUiaNode_AddRef((IWineUiaNode
*)node
);
55 out_nodes
->nodes
[out_nodes
->node_count
] = node
;
56 out_nodes
->node_count
++;
61 static HRESULT
get_safearray_dim_bounds(SAFEARRAY
*sa
, UINT dim
, LONG
*lbound
, LONG
*elems
)
67 hr
= SafeArrayGetLBound(sa
, dim
, lbound
);
71 hr
= SafeArrayGetUBound(sa
, dim
, &ubound
);
75 *elems
= (ubound
- (*lbound
)) + 1;
79 HRESULT
get_safearray_bounds(SAFEARRAY
*sa
, LONG
*lbound
, LONG
*elems
)
84 dims
= SafeArrayGetDim(sa
);
87 WARN("Invalid dimensions %d for safearray.\n", dims
);
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];
100 hr
= get_safearray_bounds(sa1
, &lbound
[0], &elems
[0]);
103 ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr
);
107 hr
= get_safearray_bounds(sa2
, &lbound
[1], &elems
[1]);
110 ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr
);
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
);
123 for (i
= 0; i
< elems
[0]; i
++)
126 hr
= SafeArrayGetElement(sa1
, &idx
, &val
[0]);
129 ERR("Failed to get element from sa1 with hr %#lx\n", hr
);
134 hr
= SafeArrayGetElement(sa2
, &idx
, &val
[1]);
137 ERR("Failed to get element from sa2 with hr %#lx\n", hr
);
141 if (val
[0] != val
[1])
142 return (val
[0] > val
[1]) - (val
[0] < val
[1]);
148 static void clear_uia_node_ptr_safearray(SAFEARRAY
*sa
, LONG elems
)
154 for (i
= 0; i
< elems
; i
++)
156 hr
= SafeArrayGetElement(sa
, &i
, &node
);
159 UiaNodeRelease(node
);
163 static void create_uia_node_safearray(VARIANT
*in
, VARIANT
*out
)
165 LONG i
, idx
, lbound
, elems
;
170 if (FAILED(get_safearray_bounds(V_ARRAY(in
), &lbound
, &elems
)))
173 if (!(sa
= SafeArrayCreateVector(VT_UINT_PTR
, 0, elems
)))
176 for (i
= 0; i
< elems
; i
++)
178 IRawElementProviderSimple
*elprov
;
182 hr
= SafeArrayGetElement(V_ARRAY(in
), &idx
, &unk
);
186 hr
= IUnknown_QueryInterface(unk
, &IID_IRawElementProviderSimple
, (void **)&elprov
);
187 IUnknown_Release(unk
);
191 hr
= UiaNodeFromProvider(elprov
, &node
);
195 IRawElementProviderSimple_Release(elprov
);
196 hr
= SafeArrayPutElement(sa
, &i
, &node
);
203 clear_uia_node_ptr_safearray(sa
, elems
);
204 SafeArrayDestroy(sa
);
208 V_VT(out
) = VT_UINT_PTR
| VT_ARRAY
;
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
;
220 hr
= SafeArrayGetUBound(V_ARRAY(in
), 1, &ubound
);
224 if (!(sa
= SafeArrayCreateVector(VT_UNKNOWN
, 0, ubound
+ 1)))
230 for (i
= 0; i
< (ubound
+ 1); i
++)
232 hr
= SafeArrayGetElement(V_ARRAY(in
), &i
, &node
);
236 hr
= SafeArrayPutElement(sa
, &i
, node
);
240 UiaNodeRelease(node
);
246 clear_uia_node_ptr_safearray(V_ARRAY(in
), ubound
+ 1);
248 SafeArrayDestroy(sa
);
254 V_VT(in
) = VT_UNKNOWN
| VT_ARRAY
;
259 static HRESULT
get_global_interface_table(IGlobalInterfaceTable
**git
)
263 hr
= CoCreateInstance(&CLSID_StdGlobalInterfaceTable
, NULL
,
264 CLSCTX_INPROC_SERVER
, &IID_IGlobalInterfaceTable
, (void **)git
);
266 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr
);
271 static HWND
get_hwnd_from_provider(IRawElementProviderSimple
*elprov
)
273 IRawElementProviderSimple
*host_prov
;
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
));
288 IRawElementProviderSimple_Release(host_prov
);
293 hr
= IRawElementProviderSimple_GetPropertyValue(elprov
, UIA_NativeWindowHandlePropertyId
, &v
);
294 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
295 hwnd
= UlongToHandle(V_I4(&v
));
302 static IRawElementProviderSimple
*get_provider_hwnd_fragment_root(IRawElementProviderSimple
*elprov
, HWND
*hwnd
)
304 IRawElementProviderFragmentRoot
*elroot
, *elroot2
;
305 IRawElementProviderSimple
*elprov2
, *ret
;
306 IRawElementProviderFragment
*elfrag
;
312 hr
= IRawElementProviderSimple_QueryInterface(elprov
, &IID_IRawElementProviderFragment
, (void **)&elfrag
);
313 if (FAILED(hr
) || !elfrag
)
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.
329 hr
= IRawElementProviderFragment_get_FragmentRoot(elfrag
, &elroot
);
330 IRawElementProviderFragment_Release(elfrag
);
331 if (FAILED(hr
) || !elroot
|| (elroot
== elroot2
))
334 hr
= IRawElementProviderFragmentRoot_QueryInterface(elroot
, &IID_IRawElementProviderSimple
, (void **)&elprov2
);
335 if (FAILED(hr
) || !elprov2
)
338 *hwnd
= get_hwnd_from_provider(elprov2
);
345 hr
= IRawElementProviderSimple_QueryInterface(elprov2
, &IID_IRawElementProviderFragment
, (void **)&elfrag
);
346 IRawElementProviderSimple_Release(elprov2
);
347 if (FAILED(hr
) || !elfrag
)
351 IRawElementProviderFragmentRoot_Release(elroot2
);
358 IRawElementProviderFragmentRoot_Release(elroot
);
360 IRawElementProviderFragmentRoot_Release(elroot2
);
365 int get_node_provider_type_at_idx(struct uia_node
*node
, int idx
)
369 for (i
= prov_idx
= 0; i
< PROV_TYPE_COUNT
; i
++)
380 ERR("Node %p has no provider at idx %d\n", node
, idx
);
384 static HRESULT
get_prop_val_from_node_provider(IWineUiaNode
*node
, const struct uia_prop_info
*prop_info
, int idx
,
387 IWineUiaProvider
*prov
;
390 VariantInit(out_val
);
391 hr
= IWineUiaNode_get_provider(node
, idx
, &prov
);
395 hr
= IWineUiaProvider_get_prop_val(prov
, prop_info
, out_val
);
396 IWineUiaProvider_Release(prov
);
401 static HRESULT
get_prov_opts_from_node_provider(IWineUiaNode
*node
, int idx
, int *out_opts
)
403 IWineUiaProvider
*prov
;
407 hr
= IWineUiaNode_get_provider(node
, idx
, &prov
);
411 hr
= IWineUiaProvider_get_prov_opts(prov
, out_opts
);
412 IWineUiaProvider_Release(prov
);
417 static HRESULT
get_has_parent_from_node_provider(IWineUiaNode
*node
, int idx
, BOOL
*out_val
)
419 IWineUiaProvider
*prov
;
423 hr
= IWineUiaNode_get_provider(node
, idx
, &prov
);
427 hr
= IWineUiaProvider_has_parent(prov
, out_val
);
428 IWineUiaProvider_Release(prov
);
433 static HRESULT
get_navigate_from_node_provider(IWineUiaNode
*node
, int idx
, int nav_dir
, VARIANT
*ret_val
)
435 IWineUiaProvider
*prov
;
438 VariantInit(ret_val
);
439 hr
= IWineUiaNode_get_provider(node
, idx
, &prov
);
443 hr
= IWineUiaProvider_navigate(prov
, nav_dir
, ret_val
);
444 IWineUiaProvider_Release(prov
);
450 * IWineUiaNode interface.
452 static HRESULT WINAPI
uia_node_QueryInterface(IWineUiaNode
*iface
, REFIID riid
, void **ppv
)
455 if (IsEqualIID(riid
, &IID_IWineUiaNode
) || IsEqualIID(riid
, &IID_IUnknown
))
458 return E_NOINTERFACE
;
460 IWineUiaNode_AddRef(iface
);
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
);
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
);
483 for (i
= 0; i
< PROV_TYPE_COUNT
; i
++)
485 if (node
->git_cookie
[i
])
487 IGlobalInterfaceTable
*git
;
490 hr
= get_global_interface_table(&git
);
493 hr
= IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git
, node
->git_cookie
[i
]);
495 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr
);
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();
514 static HRESULT WINAPI
uia_node_get_provider(IWineUiaNode
*iface
, int idx
, IWineUiaProvider
**out_prov
)
516 struct uia_node
*node
= impl_from_IWineUiaNode(iface
);
519 TRACE("(%p, %d, %p)\n", iface
, idx
, out_prov
);
522 if (node
->disconnected
)
523 return UIA_E_ELEMENTNOTAVAILABLE
;
525 if (idx
>= node
->prov_count
)
528 prov_type
= get_node_provider_type_at_idx(node
, idx
);
529 if (node
->git_cookie
[prov_type
])
531 IGlobalInterfaceTable
*git
;
532 IWineUiaProvider
*prov
;
535 hr
= get_global_interface_table(&git
);
539 hr
= IGlobalInterfaceTable_GetInterfaceFromGlobal(git
, node
->git_cookie
[prov_type
],
540 &IID_IWineUiaProvider
, (void **)&prov
);
543 ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr
);
550 *out_prov
= node
->prov
[prov_type
];
551 IWineUiaProvider_AddRef(node
->prov
[prov_type
]);
557 static HRESULT WINAPI
uia_node_get_prop_val(IWineUiaNode
*iface
, const GUID
*prop_guid
,
560 int prop_id
= UiaLookupId(AutomationIdentifierType_Property
, prop_guid
);
561 struct uia_node
*node
= impl_from_IWineUiaNode(iface
);
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
;
584 static HRESULT WINAPI
uia_node_disconnect(IWineUiaNode
*iface
)
586 struct uia_node
*node
= impl_from_IWineUiaNode(iface
);
591 if (node
->disconnected
)
593 ERR("Attempted to disconnect node which was already disconnected.\n");
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
;
604 hr
= get_global_interface_table(&git
);
607 hr
= IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git
, node
->git_cookie
[prov_type
]);
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;
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
);
634 static const IWineUiaNodeVtbl uia_node_vtbl
= {
635 uia_node_QueryInterface
,
638 uia_node_get_provider
,
639 uia_node_get_prop_val
,
644 static struct uia_node
*unsafe_impl_from_IWineUiaNode(IWineUiaNode
*iface
)
646 if (!iface
|| (iface
->lpVtbl
!= &uia_node_vtbl
))
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
)
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
;
666 else if (node
->prov
[i
])
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
++)
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
;
695 for (i
= 0; i
< PROV_TYPE_COUNT
; i
++)
697 enum ProviderOptions prov_opts
;
698 IGlobalInterfaceTable
*git
;
699 struct uia_provider
*prov
;
702 /* Only regular providers need to be queried for UseComThreading. */
703 if (!node
->prov
[i
] || is_nested_node_provider(node
->prov
[i
]))
706 prov
= impl_from_IWineUiaProvider(node
->prov
[i
]);
707 hr
= IRawElementProviderSimple_get_ProviderOptions(prov
->elprov
, &prov_opts
);
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
);
723 hr
= IGlobalInterfaceTable_RegisterInterfaceInGlobal(git
, (IUnknown
*)&prov
->IWineUiaProvider_iface
,
724 &IID_IWineUiaProvider
, &node
->git_cookie
[i
]);
733 static BOOL
node_creator_is_parent_link(struct uia_node
*node
)
735 if (node
->creator_prov_idx
== node
->parent_link_idx
)
741 static HRESULT
get_sibling_from_node_provider(struct uia_node
*node
, int prov_idx
, int nav_dir
,
748 hr
= get_navigate_from_node_provider(&node
->IWineUiaNode_iface
, prov_idx
, nav_dir
, &v
);
752 hr
= UiaHUiaNodeFromVariant(&v
, &tmp_node
);
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
))
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
);
777 hr
= UiaHUiaNodeFromVariant(&v
, &tmp_node
);
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
;
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
);
804 if (nav_dir
== NavigateDirection_FirstChild
)
809 /* If we failed to get a child, try the next provider. */
810 hr
= UiaHUiaNodeFromVariant(&v
, &tmp_node
);
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
))
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
);
828 hr
= get_sibling_from_node_provider(node_data
, node_data
->creator_prov_idx
,
829 NavigateDirection_PreviousSibling
, &v
);
831 UiaNodeRelease(tmp_node
);
835 /* If we got a valid sibling from the child provider, return it. */
836 hr
= UiaHUiaNodeFromVariant(&v
, &tmp_node
);
847 static HRESULT
navigate_uia_node(struct uia_node
*node
, int nav_dir
, HUIANODE
*out_node
)
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
);
863 hr
= get_child_for_node(node
, 0, nav_dir
, &v
);
865 WARN("Child navigation failed with hr %#lx\n", hr
);
868 case NavigateDirection_NextSibling
:
869 case NavigateDirection_PreviousSibling
:
871 struct uia_node
*node_data
;
875 hr
= get_sibling_from_node_provider(node
, node
->parent_link_idx
, nav_dir
, &v
);
878 WARN("Sibling navigation failed with hr %#lx\n", hr
);
882 if (V_VT(&v
) != VT_EMPTY
)
885 hr
= get_navigate_from_node_provider(&node
->IWineUiaNode_iface
, node
->parent_link_idx
,
886 NavigateDirection_Parent
, &tmp
);
889 WARN("Parent navigation failed with hr %#lx\n", hr
);
893 hr
= UiaHUiaNodeFromVariant(&tmp
, &parent
);
898 * If the parent node has multiple providers, attempt to get a sibling
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
);
907 hr
= get_child_for_node(node_data
, node_data
->creator_prov_idx
+ 1, NavigateDirection_LastChild
, &v
);
910 UiaNodeRelease(parent
);
914 case NavigateDirection_Parent
:
915 hr
= get_navigate_from_node_provider(&node
->IWineUiaNode_iface
, node
->parent_link_idx
, nav_dir
, &v
);
917 WARN("Parent navigation failed with hr %#lx\n", hr
);
921 WARN("Invalid NavigateDirection %d\n", nav_dir
);
925 if (V_VT(&v
) != VT_EMPTY
)
927 hr
= UiaHUiaNodeFromVariant(&v
, (HUIANODE
*)out_node
);
929 WARN("UiaHUiaNodeFromVariant failed with hr %#lx\n", hr
);
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:
946 * +-----------+-----------+
948 * +---+---+ +---+---+ +---+---+
950 * | 2 +---| 3 +---+ 4 |
952 * +---+---+ +-------+ +-------+
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
;
976 IWineUiaNode_AddRef(&node
->IWineUiaNode_iface
);
979 hr
= navigate_uia_node(node
, dir
, &node2
);
980 if (FAILED(hr
) || node2
|| !at_root_level
)
983 hr
= navigate_uia_node(node
, NavigateDirection_Parent
, &node2
);
984 if (FAILED(hr
) || !node2
)
987 hr
= uia_condition_check(node2
, ascending_stop_cond
);
988 if (FAILED(hr
) || uia_condition_matched(hr
))
990 UiaNodeRelease(node2
);
995 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
996 node
= unsafe_impl_from_IWineUiaNode((IWineUiaNode
*)node2
);
999 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
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
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
;
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
);
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.
1053 hr
= uia_condition_check(node
, search_cond
);
1057 if (uia_condition_matched(hr
))
1059 hr
= add_node_to_node_array(out_nodes
, node
);
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
);
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
)
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
))
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
);
1109 hr
= traverse_uia_node_tree_siblings(node
, ascending_stop_cond
, NavigateDirection_NextSibling
,
1110 at_root_level
, &node2
);
1112 if (FAILED(hr
) || !node2
)
1115 UiaNodeRelease(node
);
1119 UiaNodeRelease(node
);
1124 static HRESULT
bstrcat_realloc(BSTR
*bstr
, const WCHAR
*cat_str
)
1126 if (!SysReAllocStringLen(bstr
, NULL
, SysStringLen(*bstr
) + lstrlenW(cat_str
)))
1128 SysFreeString(*bstr
);
1130 return E_OUTOFMEMORY
;
1133 lstrcatW(*bstr
, cat_str
);
1137 static const WCHAR
*prov_desc_type_str
[] = {
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 };
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
);
1171 /* There's a provider preceding this one, add a "; " separator. */
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
);
1188 hr
= get_prop_val_from_node_provider(&node
->IWineUiaNode_iface
, prop_info
, i
, &v
);
1192 hr
= bstrcat_realloc(&node_desc
, V_BSTR(&v
));
1198 hr
= bstrcat_realloc(&node_desc
, L
"]");
1201 V_VT(out_desc
) = VT_BSTR
;
1202 V_BSTR(out_desc
) = node_desc
;
1207 SysFreeString(node_desc
);
1213 * IWineUiaProvider interface.
1215 static HRESULT WINAPI
uia_provider_QueryInterface(IWineUiaProvider
*iface
, REFIID riid
, void **ppv
)
1218 if (IsEqualIID(riid
, &IID_IWineUiaProvider
) || IsEqualIID(riid
, &IID_IUnknown
))
1221 return E_NOINTERFACE
;
1223 IWineUiaProvider_AddRef(iface
);
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
);
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
);
1245 IRawElementProviderSimple_Release(prov
->elprov
);
1252 static void get_variant_for_node(HUIANODE node
, VARIANT
*v
)
1256 V_I8(v
) = (UINT64
)node
;
1259 V_I4(v
) = (UINT32
)node
;
1263 static HRESULT
get_variant_for_elprov_node(IRawElementProviderSimple
*elprov
, BOOL out_nested
,
1270 hr
= create_uia_node_from_elprov(elprov
, &node
, !out_nested
);
1271 IRawElementProviderSimple_Release(elprov
);
1276 LRESULT lr
= uia_lresult_from_node(node
);
1285 get_variant_for_node(node
, v
);
1291 static HRESULT
uia_provider_get_elem_prop_val(struct uia_provider
*prov
,
1292 const struct uia_prop_info
*prop_info
, VARIANT
*ret_val
)
1298 hr
= IRawElementProviderSimple_GetPropertyValue(prov
->elprov
, prop_info
->prop_id
, &v
);
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
));
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
));
1322 case UIAutomationType_Double
:
1323 if (V_VT(&v
) != VT_R8
)
1325 WARN("Invalid vt %d for UIAutomationType_Double\n", V_VT(&v
));
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
));
1340 case UIAutomationType_Bool
:
1341 if (V_VT(&v
) != VT_BOOL
)
1343 WARN("Invalid vt %d for UIAutomationType_Bool\n", V_VT(&v
));
1349 case UIAutomationType_String
:
1350 if (V_VT(&v
) != VT_BSTR
)
1352 WARN("Invalid vt %d for UIAutomationType_String\n", V_VT(&v
));
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
));
1368 hr
= IUnknown_QueryInterface(V_UNKNOWN(&v
), &IID_IRawElementProviderSimple
,
1374 hr
= get_variant_for_elprov_node(elprov
, prov
->return_nested_node
, ret_val
);
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
));
1387 create_uia_node_safearray(&v
, ret_val
);
1388 if (V_VT(ret_val
) == (VT_UINT_PTR
| VT_ARRAY
))
1397 if (V_VT(ret_val
) == VT_EMPTY
)
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
)
1409 switch (prop_info
->prop_id
)
1411 case UIA_RuntimeIdPropertyId
:
1413 IRawElementProviderFragment
*elfrag
;
1418 hr
= IRawElementProviderSimple_QueryInterface(prov
->elprov
, &IID_IRawElementProviderFragment
, (void **)&elfrag
);
1419 if (FAILED(hr
) || !elfrag
)
1422 hr
= IRawElementProviderFragment_GetRuntimeId(elfrag
, &sa
);
1423 IRawElementProviderFragment_Release(elfrag
);
1424 if (FAILED(hr
) || !sa
)
1427 hr
= SafeArrayGetLBound(sa
, 1, &lbound
);
1430 SafeArrayDestroy(sa
);
1434 hr
= SafeArrayGetElement(sa
, &lbound
, &val
);
1437 SafeArrayDestroy(sa
);
1441 if (val
== UiaAppendRuntimeId
)
1443 enum ProviderOptions prov_opts
= 0;
1444 IRawElementProviderSimple
*elprov
;
1447 elprov
= get_provider_hwnd_fragment_root(prov
->elprov
, &hwnd
);
1450 SafeArrayDestroy(sa
);
1454 hr
= IRawElementProviderSimple_get_ProviderOptions(elprov
, &prov_opts
);
1455 IRawElementProviderSimple_Release(elprov
);
1457 WARN("get_ProviderOptions for root provider failed with %#lx\n", hr
);
1459 if (!(sa
= append_uia_runtime_id(sa
, hwnd
, prov_opts
)))
1463 V_VT(ret_val
) = VT_I4
| VT_ARRAY
;
1464 V_ARRAY(ret_val
) = sa
;
1468 case UIA_BoundingRectanglePropertyId
:
1470 IRawElementProviderFragment
*elfrag
;
1471 struct UiaRect rect
= { 0 };
1472 double rect_vals
[4];
1476 hr
= IRawElementProviderSimple_QueryInterface(prov
->elprov
, &IID_IRawElementProviderFragment
, (void **)&elfrag
);
1477 if (FAILED(hr
) || !elfrag
)
1480 hr
= IRawElementProviderFragment_get_BoundingRectangle(elfrag
, &rect
);
1481 IRawElementProviderFragment_Release(elfrag
);
1482 if (FAILED(hr
) || (rect
.width
<= 0 || rect
.height
<= 0))
1485 if (!(sa
= SafeArrayCreateVector(VT_R8
, 0, ARRAY_SIZE(rect_vals
))))
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
]);
1497 SafeArrayDestroy(sa
);
1502 V_VT(ret_val
) = VT_R8
| VT_ARRAY
;
1503 V_ARRAY(ret_val
) = sa
;
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";
1515 hr
= uia_provider_get_elem_prop_val(prov
, prop_info
, &v
);
1519 if (V_VT(&v
) == VT_BSTR
)
1520 prov_desc_str
= SysAllocStringLen(V_BSTR(&v
), lstrlenW(V_BSTR(&v
)) + lstrlenW(provider_origin
));
1522 prov_desc_str
= SysAllocStringLen(default_desc
, lstrlenW(default_desc
) + lstrlenW(provider_origin
));
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
;
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
;
1549 unk
= pattern_prov
= NULL
;
1550 hr
= IRawElementProviderSimple_GetPatternProvider(prov
->elprov
, prop_info
->pattern_id
, &unk
);
1551 if (FAILED(hr
) || !unk
)
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");
1562 switch (prop_info
->prop_id
)
1564 case UIA_ValueIsReadOnlyPropertyId
:
1568 hr
= IValueProvider_get_IsReadOnly((IValueProvider
*)pattern_prov
, &val
);
1570 variant_init_bool(ret_val
, val
);
1578 IUnknown_Release(pattern_prov
);
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
);
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
;
1614 TRACE("%p, %p\n", iface
, out_opts
);
1617 hr
= IRawElementProviderSimple_get_ProviderOptions(prov
->elprov
, &prov_opts
);
1619 *out_opts
= prov_opts
;
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
;
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
;
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
;
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
)
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
)
1680 hr
= get_variant_for_elprov_node(elprov
, prov
->return_nested_node
, out_val
);
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
,
1701 struct uia_provider
*prov
= heap_alloc_zero(sizeof(*prov
));
1704 return E_OUTOFMEMORY
;
1706 prov
->IWineUiaProvider_iface
.lpVtbl
= &uia_provider_vtbl
;
1707 prov
->elprov
= elprov
;
1709 node
->prov
[prov_type
] = &prov
->IWineUiaProvider_iface
;
1710 if (!node
->prov_count
)
1711 node
->creator_prov_type
= prov_type
;
1714 IRawElementProviderSimple_AddRef(elprov
);
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
;
1731 hr
= IRawElementProviderSimple_get_ProviderOptions(elprov
, &prov_opts
);
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
;
1747 prov_type
= PROV_TYPE_MAIN
;
1749 node
= heap_alloc_zero(sizeof(*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
);
1759 hr
= create_wine_uia_provider(node
, elprov
, prov_type
);
1766 if (node
->hwnd
&& get_hwnd_providers
)
1768 hr
= uia_get_providers_for_hwnd(node
);
1770 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr
);
1773 hr
= prepare_uia_node(node
);
1776 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
1780 *out_node
= (void *)&node
->IWineUiaNode_iface
;
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
;
1809 struct uia_get_node_prov_args
{
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
,
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
);
1842 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
1845 static DWORD WINAPI
uia_client_thread_proc(void *arg
)
1847 HANDLE initialized_event
= arg
;
1851 hwnd
= CreateWindowW(L
"Message", NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, NULL
, NULL
, NULL
);
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
)
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
;
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
);
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
);
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
);
1927 CloseHandle(ready_event
);
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
);
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
;
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
)
1976 if (IsEqualIID(riid
, &IID_IWineUiaProvider
) || IsEqualIID(riid
, &IID_IUnknown
))
1979 return E_NOINTERFACE
;
1981 IWineUiaProvider_AddRef(iface
);
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
);
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
);
2002 IWineUiaNode_Release(prov
->nested_node
);
2003 uia_stop_client_thread();
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
);
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");
2027 hr
= IWineUiaNode_get_prop_val(prov
->nested_node
, prop_info
->guid
, &v
);
2031 switch (prop_info
->type
)
2033 case UIAutomationType_Element
:
2037 hr
= uia_node_from_lresult((LRESULT
)V_I4(&v
), &node
);
2041 get_variant_for_node(node
, ret_val
);
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
);
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
)
2086 hr
= uia_node_from_lresult((LRESULT
)V_I4(&v
), &node
);
2090 get_variant_for_node(node
, out_val
);
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
)
2114 static HRESULT
create_wine_uia_nested_node_provider(struct uia_node
*node
, LRESULT lr
,
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
;
2125 hr
= ObjectFromLresult(lr
, &IID_IWineUiaNode
, 0, (void **)&nested_node
);
2128 uia_stop_client_thread();
2132 hr
= get_prov_opts_from_node_provider(nested_node
, 0, &prov_opts
);
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();
2141 /* Nested nodes can only serve as override or main providers. */
2142 if (prov_opts
& ProviderOptions_OverrideProvider
)
2143 prov_type
= PROV_TYPE_OVERRIDE
;
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();
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.
2162 struct uia_node
*node_data
= unsafe_impl_from_IWineUiaNode(nested_node
);
2163 struct uia_provider
*prov_data
;
2167 ERR("Failed to get uia_node structure from nested node\n");
2168 uia_stop_client_thread();
2172 provider_iface
= node_data
->prov
[get_node_provider_type_at_idx(node_data
, 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();
2185 prov
= heap_alloc_zero(sizeof(*prov
));
2187 return E_OUTOFMEMORY
;
2189 prov
->IWineUiaProvider_iface
.lpVtbl
= &uia_nested_node_provider_vtbl
;
2190 prov
->nested_node
= nested_node
;
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
);
2201 IWineUiaProvider_Release(&prov
->IWineUiaProvider_iface
);
2205 hr
= IGlobalInterfaceTable_RegisterInterfaceInGlobal(git
, (IUnknown
*)&prov
->IWineUiaProvider_iface
,
2206 &IID_IWineUiaProvider
, &git_cookie
);
2209 IWineUiaProvider_Release(&prov
->IWineUiaProvider_iface
);
2217 hr
= IWineUiaNode_get_hwnd(nested_node
, &hwnd
);
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
;
2232 static HRESULT
uia_node_from_lresult(LRESULT lr
, HUIANODE
*huianode
)
2234 struct uia_node
*node
;
2238 node
= heap_alloc_zero(sizeof(*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
);
2247 uia_start_client_thread();
2248 hr
= create_wine_uia_nested_node_provider(node
, lr
, FALSE
);
2257 hr
= uia_get_providers_for_hwnd(node
);
2259 WARN("uia_get_providers_for_hwnd failed with hr %#lx\n", hr
);
2262 hr
= prepare_uia_node(node
);
2265 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
2269 *huianode
= (void *)&node
->IWineUiaNode_iface
;
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())
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
;
2296 uia_stop_client_thread();
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
;
2312 TRACE("(%p, %p)\n", hwnd
, huianode
);
2315 return E_INVALIDARG
;
2319 if (!IsWindow(hwnd
))
2320 return UIA_E_ELEMENTNOTAVAILABLE
;
2322 node
= heap_alloc_zero(sizeof(*node
));
2324 return E_OUTOFMEMORY
;
2327 node
->IWineUiaNode_iface
.lpVtbl
= &uia_node_vtbl
;
2328 list_init(&node
->prov_thread_list_entry
);
2329 list_init(&node
->node_map_list_entry
);
2332 hr
= uia_get_providers_for_hwnd(node
);
2339 hr
= prepare_uia_node(node
);
2342 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
2346 *huianode
= (void *)&node
->IWineUiaNode_iface
;
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
);
2363 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
2367 static HRESULT
get_prop_val_from_node(struct uia_node
*node
,
2368 const struct uia_prop_info
*prop_info
, VARIANT
*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
)
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
;
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
);
2404 return E_INVALIDARG
;
2406 if (!prop_info
->type
)
2408 FIXME("No type info for prop_id %d\n", prop_id
);
2414 case UIA_RuntimeIdPropertyId
:
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
;
2427 case UIA_ProviderDescriptionPropertyId
:
2428 hr
= get_node_provider_description_string(node
, &v
);
2429 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_BSTR
))
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
2445 if (prop_info
->type
== UIAutomationType_ElementArray
)
2447 uia_node_ptr_to_unk_safearray(&v
);
2448 if (V_VT(&v
) != VT_EMPTY
)
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
) };
2472 for (idx
= 0; idx
< ARRAY_SIZE(rt_id
); idx
++)
2474 hr
= SafeArrayPutElement(sa
, &idx
, (void *)&rt_id
[idx
]);
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
;
2490 hr
= get_safearray_bounds(sa
, &lbound
, &elems
);
2494 /* elems includes the UiaAppendRuntimeId value, so we only add 2. */
2495 if (!(sa2
= SafeArrayCreateVector(VT_I4
, 0, elems
+ 2)))
2498 hr
= write_runtime_id_base(sa2
, hwnd
);
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
;
2507 val
= FRAGMENT_ROOT_MAIN_TYPE_ID
;
2510 hr
= SafeArrayPutElement(sa2
, &idx
, &val
);
2514 for (i
= 0; i
< (elems
- 1); i
++)
2516 idx
= (lbound
+ 1) + i
;
2517 hr
= SafeArrayGetElement(sa
, &idx
, &val
);
2522 hr
= SafeArrayPutElement(sa2
, &idx
, &val
);
2532 SafeArrayDestroy(sa2
);
2534 SafeArrayDestroy(sa
);
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
);
2547 TRACE("(%p, %p)\n", huianode
, runtime_id
);
2549 if (!node
|| !runtime_id
)
2550 return E_INVALIDARG
;
2554 /* Provide an HWND based runtime ID if the node has an HWND. */
2559 if (!(sa
= SafeArrayCreateVector(VT_I4
, 0, 2)))
2562 hr
= write_runtime_id_base(sa
, node
->hwnd
);
2565 SafeArrayDestroy(sa
);
2576 hr
= get_prop_val_from_node(node
, prop_info
, &v
);
2583 if (V_VT(&v
) == (VT_I4
| VT_ARRAY
))
2584 *runtime_id
= V_ARRAY(&v
);
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
;
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
);
2618 *huianode
= (HUIANODE
)V_I8(in_val
);
2620 *huianode
= (HUIANODE
)V_I4(in_val
);
2627 static SAFEARRAY WINAPI
*default_uia_provider_callback(HWND hwnd
, enum ProviderType prov_type
)
2631 case ProviderType_Proxy
:
2632 FIXME("Default ProviderType_Proxy MSAA provider unimplemented.\n");
2635 case ProviderType_NonClientArea
:
2636 FIXME("Default ProviderType_NonClientArea provider unimplemented.\n");
2639 case ProviderType_BaseHwnd
:
2640 FIXME("Default ProviderType_BaseHwnd provider unimplemented.\n");
2650 static UiaProviderCallback
*uia_provider_callback
= default_uia_provider_callback
;
2652 static HRESULT
uia_get_clientside_provider(struct uia_node
*node
, int prov_type
,
2655 IRawElementProviderSimple
*elprov
;
2662 if (!(sa
= uia_provider_callback(node
->hwnd
, prov_type
)))
2665 hr
= SafeArrayGetVartype(sa
, &vt
);
2666 if (FAILED(hr
) || (vt
!= VT_UNKNOWN
))
2669 hr
= get_safearray_bounds(sa
, &lbound
, &elems
);
2673 /* Returned SAFEARRAY can only have 1 element. */
2676 WARN("Invalid element count %ld for returned SAFEARRAY\n", elems
);
2680 hr
= SafeArrayGetElement(sa
, &lbound
, &unk
);
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");
2693 hr
= create_wine_uia_provider(node
, elprov
, node_prov_type
);
2694 IRawElementProviderSimple_Release(elprov
);
2698 WARN("Failed to get clientside provider, hr %#lx\n", hr
);
2699 SafeArrayDestroy(sa
);
2703 static HRESULT
uia_get_providers_for_hwnd(struct uia_node
*node
)
2707 hr
= uia_get_provider_from_hwnd(node
);
2711 if (!node
->prov
[PROV_TYPE_MAIN
])
2713 hr
= uia_get_clientside_provider(node
, ProviderType_Proxy
, PROV_TYPE_MAIN
);
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
);
2728 if (!node
->prov
[PROV_TYPE_HWND
])
2730 hr
= uia_get_clientside_provider(node
, ProviderType_BaseHwnd
, PROV_TYPE_HWND
);
2735 if (!node
->prov_count
)
2737 if (uia_provider_callback
== default_uia_provider_callback
)
2746 /***********************************************************************
2747 * UiaRegisterProviderCallback (uiautomationcore.@)
2749 void WINAPI
UiaRegisterProviderCallback(UiaProviderCallback
*callback
)
2751 TRACE("(%p)\n", callback
);
2754 uia_provider_callback
= callback
;
2756 uia_provider_callback
= default_uia_provider_callback
;
2759 static BOOL
uia_condition_matched(HRESULT hr
)
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
);
2774 return E_INVALIDARG
;
2776 switch (prop_info
->type
)
2778 case UIAutomationType_Bool
:
2779 case UIAutomationType_IntArray
:
2783 FIXME("PropertyCondition comparison unimplemented for type %#x\n", prop_info
->type
);
2787 hr
= UiaGetPropertyValue(node
, prop_info
->prop_id
, &v
);
2788 if (FAILED(hr
) || V_VT(&v
) == VT_UNKNOWN
)
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
))
2802 case UIAutomationType_IntArray
:
2803 if (!uia_compare_safearrays(V_ARRAY(&v
), V_ARRAY(&prop_cond
->Value
), prop_info
->type
))
2820 static HRESULT
uia_condition_check(HUIANODE node
, struct UiaCondition
*condition
)
2824 switch (condition
->ConditionType
)
2826 case ConditionType_True
:
2829 case ConditionType_False
:
2832 case ConditionType_Not
:
2834 struct UiaNotCondition
*not_cond
= (struct UiaNotCondition
*)condition
;
2836 hr
= uia_condition_check(node
, not_cond
->pConditions
);
2840 if (uia_condition_matched(hr
))
2846 case ConditionType_And
:
2847 case ConditionType_Or
:
2849 struct UiaAndOrCondition
*and_or_cond
= (struct UiaAndOrCondition
*)condition
;
2852 for (i
= 0; i
< and_or_cond
->cConditions
; i
++)
2854 hr
= uia_condition_check(node
, and_or_cond
->ppConditions
[i
]);
2858 if (condition
->ConditionType
== ConditionType_And
&& !uia_condition_matched(hr
))
2860 else if (condition
->ConditionType
== ConditionType_Or
&& uia_condition_matched(hr
))
2864 if (condition
->ConditionType
== ConditionType_Or
)
2870 case ConditionType_Property
:
2871 return uia_property_condition_check(node
, (struct UiaPropertyCondition
*)condition
);
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];
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
;
2902 if (cache_req
->Scope
!= TreeScope_Element
)
2904 FIXME("Unsupported cache request scope %#x\n", cache_req
->Scope
);
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
:
2926 case NormalizeState_View
:
2927 cond
= cache_req
->pViewCondition
;
2930 case NormalizeState_Custom
:
2931 cond
= normalize_cond
;
2935 WARN("Invalid normalize_state %d\n", normalize_state
);
2936 return E_INVALIDARG
;
2941 hr
= uia_condition_check(huianode
, cond
);
2945 if (!uia_condition_matched(hr
))
2947 *tree_struct
= SysAllocString(L
"");
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");
2961 get_variant_for_node(huianode
, &v
);
2962 idx
[0] = idx
[1] = 0;
2964 hr
= SafeArrayPutElement(sa
, idx
, &v
);
2967 SafeArrayDestroy(sa
);
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
);
2984 hr
= SafeArrayPutElement(sa
, idx
, &v
);
2988 SafeArrayDestroy(sa
);
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
);
3000 *tree_struct
= SysAllocString(L
"P)");
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
);
3015 TRACE("(%p, %u, %p, %p, %p, %p)\n", huianode
, dir
, nav_condition
, cache_req
, out_req
,
3018 if (!node
|| !nav_condition
|| !cache_req
|| !out_req
|| !tree_struct
)
3019 return E_INVALIDARG
;
3022 *tree_struct
= NULL
;
3024 if (nav_condition
->ConditionType
!= ConditionType_True
)
3026 FIXME("ConditionType %d based navigation is not implemented.\n", nav_condition
->ConditionType
);
3030 hr
= navigate_uia_node(node
, dir
, &node2
);
3036 hr
= UiaGetUpdatedCache(node2
, cache_req
, NormalizeState_None
, NULL
, out_req
, tree_struct
);
3038 WARN("UiaGetUpdatedCache failed with hr %#lx\n", hr
);
3039 UiaNodeRelease(node2
);
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
;
3053 for (i
= cur_offset
= 0; i
< reqs_count
; i
++)
3058 for (x
= 0; x
< 2; x
++)
3060 hr
= get_safearray_dim_bounds(reqs
[i
], 1 + x
, &lbound
[x
], &elems
[x
]);
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
);
3075 idx
[0] = x
+ cur_offset
;
3077 hr
= SafeArrayPutElement(out_req
, idx
, &v
);
3083 cur_offset
+= elems
[0];
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;
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
;
3116 * If the initial node has a runtime ID, we'll use it as a stop
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
;
3127 sibling_stop_cond
= (struct UiaCondition
*)&UiaFalseCondition
;
3129 if (find_params
->ExcludeRoot
)
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
)
3141 if (!(offsets
= SafeArrayCreateVector(VT_I4
, 0, nodes
.node_count
)))
3147 if (!(tree_structs
= SafeArrayCreateVector(VT_BSTR
, 0, nodes
.node_count
)))
3153 if (!(tmp_reqs
= heap_alloc_zero(sizeof(*tmp_reqs
) * nodes
.node_count
)))
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
);
3171 hr
= SafeArrayPutElement(tree_structs
, &idx
, tree_struct
);
3172 SysFreeString(tree_struct
);
3176 hr
= SafeArrayPutElement(offsets
, &idx
, &cur_offset
);
3183 hr
= get_safearray_dim_bounds(tmp_reqs
[i
], 1, &lbound
, &elems
);
3187 cur_offset
+= elems
;
3190 if (nodes
.node_count
== 1)
3193 heap_free(tmp_reqs
);
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
)))
3207 hr
= uia_cache_request_combine(tmp_reqs
, nodes
.node_count
, req
);
3212 *out_tree_structs
= tree_structs
;
3213 *out_offsets
= offsets
;
3217 VariantClear(&prop_cond
.Value
);
3218 clear_node_array(&nodes
);
3222 for (i
= 0; i
< nodes
.node_count
; i
++)
3223 SafeArrayDestroy(tmp_reqs
[i
]);
3224 heap_free(tmp_reqs
);
3229 SafeArrayDestroy(tree_structs
);
3230 SafeArrayDestroy(offsets
);
3231 SafeArrayDestroy(req
);
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
);
3248 /***********************************************************************
3249 * UiaRemoveEvent (uiautomationcore.@)
3251 HRESULT WINAPI
UiaRemoveEvent(HUIAEVENT huiaevent
)
3253 FIXME("(%p): stub\n", huiaevent
);
3257 /***********************************************************************
3258 * UiaEventAddWindow (uiautomationcore.@)
3260 HRESULT WINAPI
UiaEventAddWindow(HUIAEVENT huiaevent
, HWND hwnd
)
3262 FIXME("(%p, %p): stub\n", huiaevent
, hwnd
);
3266 /***********************************************************************
3267 * UiaEventRemoveWindow (uiautomationcore.@)
3269 HRESULT WINAPI
UiaEventRemoveWindow(HUIAEVENT huiaevent
, HWND hwnd
)
3271 FIXME("(%p, %p): stub\n", huiaevent
, hwnd
);