uiautomationcore: Add support for multiple providers on a single HUIANODE.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2022-10-12 12:33:52 -04:00 committed by Alexandre Julliard
parent e9ff761d51
commit edcd55bae2
5 changed files with 231 additions and 104 deletions

View file

@ -4328,14 +4328,14 @@ static const struct prov_method_sequence get_elem_arr_prop_seq[] = {
{ &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS },
{ &Provider_child2, PROV_GET_PROVIDER_OPTIONS },
/* Win10v1507 and below call this. */
{ &Provider_child2, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child2, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child2, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child2, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child2, PROV_GET_PROVIDER_OPTIONS },
{ &Provider_child, PROV_GET_PROPERTY_VALUE },
{ &Provider_child2, PROV_GET_PROPERTY_VALUE },
{ 0 }
@ -4972,8 +4972,8 @@ static const struct prov_method_sequence node_from_hwnd2[] = {
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
/* Windows 10+ calls this. */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL },
@ -4987,7 +4987,7 @@ static const struct prov_method_sequence node_from_hwnd3[] = {
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */
{ 0 }
@ -5016,7 +5016,7 @@ static const struct prov_method_sequence node_from_hwnd5[] = {
{ &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS },
/* Only done in Windows 8+. */
{ &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL },
{ &Provider_child, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL },
@ -5034,7 +5034,7 @@ static const struct prov_method_sequence node_from_hwnd6[] = {
{ &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS },
/* Next 4 are only done in Windows 8+. */
{ &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL },
{ &Provider_child, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL },
@ -5059,7 +5059,7 @@ static const struct prov_method_sequence node_from_hwnd7[] = {
{ &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
@ -5077,7 +5077,7 @@ static const struct prov_method_sequence node_from_hwnd8[] = {
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ControlTypePropertyId */
@ -5091,7 +5091,7 @@ static const struct prov_method_sequence node_from_hwnd9[] = {
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
/* Only done in Windows 8+. */
{ &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL },
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL },
@ -5109,7 +5109,7 @@ static const struct prov_method_sequence disconnect_prov1[] = {
{ &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider_child, PROV_GET_PROVIDER_OPTIONS },
{ &Provider_child, FRAG_GET_RUNTIME_ID },
{ &Provider_child, FRAG_GET_FRAGMENT_ROOT },
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
@ -5124,7 +5124,7 @@ static const struct prov_method_sequence disconnect_prov2[] = {
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, FRAG_GET_RUNTIME_ID },
{ &Provider, FRAG_GET_FRAGMENT_ROOT },
{ 0 }
@ -5137,7 +5137,7 @@ static const struct prov_method_sequence disconnect_prov3[] = {
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ &Provider, FRAG_GET_RUNTIME_ID },
{ 0 }
};
@ -5148,7 +5148,7 @@ static const struct prov_method_sequence disconnect_prov4[] = {
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER },
{ &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO },
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
{ 0 }
};

View file

@ -54,7 +54,7 @@ library UIA_wine_private
]
interface IWineUiaNode : IUnknown
{
HRESULT get_provider([out, retval]IWineUiaProvider **out_prov);
HRESULT get_provider([in]int idx, [out, retval]IWineUiaProvider **out_prov);
HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val);
HRESULT disconnect();
}

View file

@ -314,6 +314,25 @@ static IRawElementProviderSimple *get_provider_hwnd_fragment_root(IRawElementPro
return ret;
}
int get_node_provider_type_at_idx(struct uia_node *node, int idx)
{
int i, prov_idx;
for (i = prov_idx = 0; i < PROV_TYPE_COUNT; i++)
{
if (node->prov[i])
{
if (prov_idx == idx)
return i;
else
prov_idx++;
}
}
ERR("Node %p has no provider at idx %d\n", node, idx);
return 0;
}
/*
* IWineUiaNode interface.
*/
@ -346,22 +365,28 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
TRACE("%p, refcount %ld\n", node, ref);
if (!ref)
{
if (node->git_cookie)
{
IGlobalInterfaceTable *git;
HRESULT hr;
int i;
hr = get_global_interface_table(&git);
if (SUCCEEDED(hr))
for (i = 0; i < PROV_TYPE_COUNT; i++)
{
if (node->git_cookie[i])
{
hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie);
if (FAILED(hr))
WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
IGlobalInterfaceTable *git;
HRESULT hr;
hr = get_global_interface_table(&git);
if (SUCCEEDED(hr))
{
hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[i]);
if (FAILED(hr))
WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
}
}
if (node->prov[i])
IWineUiaProvider_Release(node->prov[i]);
}
if (node->prov)
IWineUiaProvider_Release(node->prov);
if (!list_empty(&node->prov_thread_list_entry))
uia_provider_thread_remove_node((HUIANODE)iface);
if (node->nested_node)
@ -373,17 +398,22 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
return ref;
}
static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvider **out_prov)
static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, int idx, IWineUiaProvider **out_prov)
{
struct uia_node *node = impl_from_IWineUiaNode(iface);
int prov_type;
TRACE("(%p, %d, %p)\n", iface, idx, out_prov);
*out_prov = NULL;
if (node->disconnected)
{
*out_prov = NULL;
return UIA_E_ELEMENTNOTAVAILABLE;
}
if (node->git_cookie)
if (idx >= node->prov_count)
return E_INVALIDARG;
prov_type = get_node_provider_type_at_idx(node, idx);
if (node->git_cookie[prov_type])
{
IGlobalInterfaceTable *git;
IWineUiaProvider *prov;
@ -393,7 +423,7 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide
if (FAILED(hr))
return hr;
hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie,
hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie[prov_type],
&IID_IWineUiaProvider, (void **)&prov);
if (FAILED(hr))
{
@ -404,8 +434,8 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide
}
else
{
*out_prov = node->prov;
IWineUiaProvider_AddRef(node->prov);
*out_prov = node->prov[prov_type];
IWineUiaProvider_AddRef(node->prov[prov_type]);
}
return S_OK;
@ -441,6 +471,7 @@ static HRESULT WINAPI uia_node_get_prop_val(IWineUiaNode *iface, const GUID *pro
static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface)
{
struct uia_node *node = impl_from_IWineUiaNode(iface);
int prov_type;
TRACE("%p\n", node);
@ -450,7 +481,9 @@ static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface)
return E_FAIL;
}
if (node->git_cookie)
/* Nested nodes can only have one provider. */
prov_type = get_node_provider_type_at_idx(node, 0);
if (node->git_cookie[prov_type])
{
IGlobalInterfaceTable *git;
HRESULT hr;
@ -458,16 +491,18 @@ static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface)
hr = get_global_interface_table(&git);
if (SUCCEEDED(hr))
{
hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie);
hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[prov_type]);
if (FAILED(hr))
WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
}
node->git_cookie = 0;
node->git_cookie[prov_type] = 0;
}
IWineUiaProvider_Release(node->prov);
node->prov = NULL;
IWineUiaProvider_Release(node->prov[prov_type]);
node->prov[prov_type] = NULL;
node->disconnected = TRUE;
node->prov_count = 0;
return S_OK;
}
@ -489,6 +524,49 @@ static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface)
return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface);
}
static BOOL is_nested_node_provider(IWineUiaProvider *iface);
static HRESULT prepare_uia_node(struct uia_node *node)
{
int i;
for (i = 0; i < PROV_TYPE_COUNT; i++)
{
enum ProviderOptions prov_opts;
IGlobalInterfaceTable *git;
struct uia_provider *prov;
HRESULT hr;
/* Only regular providers need to be queried for UseComThreading. */
if (!node->prov[i] || is_nested_node_provider(node->prov[i]))
continue;
prov = impl_from_IWineUiaProvider(node->prov[i]);
hr = IRawElementProviderSimple_get_ProviderOptions(prov->elprov, &prov_opts);
if (FAILED(hr))
continue;
/*
* If the UseComThreading ProviderOption is specified, all calls to the
* provided IRawElementProviderSimple need to respect the apartment type
* of the thread that creates the HUIANODE. i.e, if it's created in an
* STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
*/
if (prov_opts & ProviderOptions_UseComThreading)
{
hr = get_global_interface_table(&git);
if (FAILED(hr))
return hr;
hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
&IID_IWineUiaProvider, &node->git_cookie[i]);
if (FAILED(hr))
return hr;
}
}
return S_OK;
}
/*
* IWineUiaProvider interface.
*/
@ -768,55 +846,19 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = {
uia_provider_get_prop_val,
};
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov)
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov,
int prov_type)
{
static const int supported_prov_opts = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
enum ProviderOptions prov_opts;
struct uia_provider *prov;
HRESULT hr;
struct uia_provider *prov = heap_alloc_zero(sizeof(*prov));
hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
if (FAILED(hr))
return hr;
if (prov_opts & ~supported_prov_opts)
FIXME("Ignoring unsupported ProviderOption(s) %#x\n", prov_opts & ~supported_prov_opts);
prov = heap_alloc_zero(sizeof(*prov));
if (!prov)
return E_OUTOFMEMORY;
prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
prov->elprov = elprov;
prov->ref = 1;
node->prov = &prov->IWineUiaProvider_iface;
node->hwnd = get_hwnd_from_provider(elprov);
/*
* If the UseComThreading ProviderOption is specified, all calls to the
* provided IRawElementProviderSimple need to respect the apartment type
* of the thread that creates the HUIANODE. i.e, if it's created in an
* STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
*/
if (prov_opts & ProviderOptions_UseComThreading)
{
IGlobalInterfaceTable *git;
hr = get_global_interface_table(&git);
if (FAILED(hr))
{
heap_free(prov);
return hr;
}
hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
&IID_IWineUiaProvider, &node->git_cookie);
if (FAILED(hr))
{
heap_free(prov);
return hr;
}
}
node->prov[prov_type] = &prov->IWineUiaProvider_iface;
node->prov_count++;
IRawElementProviderSimple_AddRef(elprov);
return S_OK;
@ -827,7 +869,11 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid
*/
HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *huianode)
{
static const int unsupported_prov_opts = ProviderOptions_ProviderOwnsSetFocus | ProviderOptions_HasNativeIAccessible |
ProviderOptions_UseClientCoordinates;
enum ProviderOptions prov_opts;
struct uia_node *node;
int prov_type;
HRESULT hr;
TRACE("(%p, %p)\n", elprov, huianode);
@ -837,25 +883,51 @@ HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *
*huianode = NULL;
hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
if (FAILED(hr))
return hr;
if (prov_opts & unsupported_prov_opts)
FIXME("Ignoring unsupported ProviderOption(s) %#x\n", prov_opts & unsupported_prov_opts);
if (prov_opts & ProviderOptions_OverrideProvider)
prov_type = PROV_TYPE_OVERRIDE;
else if (prov_opts & ProviderOptions_NonClientAreaProvider)
prov_type = PROV_TYPE_NONCLIENT;
else if (prov_opts & ProviderOptions_ServerSideProvider)
prov_type = PROV_TYPE_MAIN;
else if (prov_opts & ProviderOptions_ClientSideProvider)
prov_type = PROV_TYPE_HWND;
else
prov_type = PROV_TYPE_MAIN;
node = heap_alloc_zero(sizeof(*node));
if (!node)
return E_OUTOFMEMORY;
hr = create_wine_uia_provider(node, elprov);
node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
node->hwnd = get_hwnd_from_provider(elprov);
list_init(&node->prov_thread_list_entry);
list_init(&node->node_map_list_entry);
node->ref = 1;
hr = create_wine_uia_provider(node, elprov, prov_type);
if (FAILED(hr))
{
heap_free(node);
return hr;
}
node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
list_init(&node->prov_thread_list_entry);
list_init(&node->node_map_list_entry);
node->ref = 1;
hr = prepare_uia_node(node);
if (FAILED(hr))
{
IWineUiaNode_Release(&node->IWineUiaNode_iface);
return hr;
}
*huianode = (void *)&node->IWineUiaNode_iface;
return hr;
return S_OK;
}
/*
@ -1121,6 +1193,14 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
uia_nested_node_provider_get_prop_val,
};
static BOOL is_nested_node_provider(IWineUiaProvider *iface)
{
if (iface->lpVtbl == &uia_nested_node_provider_vtbl)
return TRUE;
return FALSE;
}
static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr,
BOOL unwrap)
{
@ -1155,14 +1235,14 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU
return E_FAIL;
}
IWineUiaProvider_AddRef(node_data->prov);
provider_iface = node_data->prov;
git_cookie = node_data->git_cookie;
prov_data = impl_from_IWineUiaProvider(node_data->prov);
provider_iface = node_data->prov[get_node_provider_type_at_idx(node_data, 0)];
git_cookie = 0;
IWineUiaProvider_AddRef(provider_iface);
prov_data = impl_from_IWineUiaProvider(provider_iface);
prov_data->return_nested_node = FALSE;
node_data->git_cookie = 0;
IWineUiaNode_Release(&node_data->IWineUiaNode_iface);
IWineUiaNode_Release(nested_node);
uia_stop_client_thread();
}
else
@ -1196,8 +1276,9 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU
}
}
node->prov = provider_iface;
node->git_cookie = git_cookie;
node->prov[PROV_TYPE_MAIN] = provider_iface;
node->git_cookie[PROV_TYPE_MAIN] = git_cookie;
node->prov_count++;
return S_OK;
}
@ -1225,6 +1306,13 @@ static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode)
return hr;
}
hr = prepare_uia_node(node);
if (FAILED(hr))
{
IWineUiaNode_Release(&node->IWineUiaNode_iface);
return hr;
}
*huianode = (void *)&node->IWineUiaNode_iface;
return hr;
@ -1296,6 +1384,13 @@ HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode)
return hr;
}
hr = prepare_uia_node(node);
if (FAILED(hr))
{
IWineUiaNode_Release(&node->IWineUiaNode_iface);
return hr;
}
*huianode = (void *)&node->IWineUiaNode_iface;
return S_OK;
@ -1321,15 +1416,21 @@ static HRESULT get_prop_val_from_node_provider(struct uia_node *node,
const struct uia_prop_info *prop_info, VARIANT *v)
{
IWineUiaProvider *prov;
HRESULT hr;
HRESULT hr = S_OK;
int i;
hr = IWineUiaNode_get_provider(&node->IWineUiaNode_iface, &prov);
if (FAILED(hr))
return hr;
for (i = 0; i < node->prov_count; i++)
{
hr = IWineUiaNode_get_provider(&node->IWineUiaNode_iface, i, &prov);
if (FAILED(hr))
return hr;
VariantInit(v);
hr = IWineUiaProvider_get_prop_val(prov, prop_info, v);
IWineUiaProvider_Release(prov);
VariantInit(v);
hr = IWineUiaProvider_get_prop_val(prov, prop_info, v);
IWineUiaProvider_Release(prov);
if (FAILED(hr) || V_VT(v) != VT_EMPTY)
break;
}
return hr;
}

View file

@ -30,12 +30,35 @@ enum uia_prop_type {
PROP_TYPE_SPECIAL,
};
/*
* HUIANODEs that have an associated HWND are able to pull data from up to 4
* different providers:
*
* - Override providers are used to override values from all other providers.
* - Main providers are the base provider for an HUIANODE.
* - Nonclient providers are used to represent the nonclient area of the HWND.
* - HWND providers are used to represent data from the HWND as a whole, such
* as the bounding box.
*
* When a property is requested from the node, each provider is queried in
* descending order starting with the override provider until either one
* returns a property or there are no more providers to query.
*/
enum uia_node_prov_type {
PROV_TYPE_OVERRIDE,
PROV_TYPE_MAIN,
PROV_TYPE_NONCLIENT,
PROV_TYPE_HWND,
PROV_TYPE_COUNT,
};
struct uia_node {
IWineUiaNode IWineUiaNode_iface;
LONG ref;
IWineUiaProvider *prov;
DWORD git_cookie;
IWineUiaProvider *prov[PROV_TYPE_COUNT];
DWORD git_cookie[PROV_TYPE_COUNT];
int prov_count;
HWND hwnd;
BOOL nested_node;
@ -65,6 +88,7 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *
/* uia_client.c */
int uia_compare_runtime_ids(SAFEARRAY *sa1, SAFEARRAY *sa2) DECLSPEC_HIDDEN;
int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN;
/* uia_ids.c */
const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;

View file

@ -1231,10 +1231,12 @@ exit:
static HRESULT uia_provider_thread_add_node(HUIANODE node)
{
struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node);
struct uia_provider *prov_data = impl_from_IWineUiaProvider(node_data->prov);
int prov_type = get_node_provider_type_at_idx(node_data, 0);
struct uia_provider *prov_data;
SAFEARRAY *sa;
HRESULT hr;
prov_data = impl_from_IWineUiaProvider(node_data->prov[prov_type]);
node_data->nested_node = prov_data->return_nested_node = TRUE;
hr = UiaGetRuntimeId(node, &sa);
if (FAILED(hr))