From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 28 +-- dlls/uiautomationcore/uia_classes.idl | 2 +- dlls/uiautomationcore/uia_client.c | 273 ++++++++++++++------- dlls/uiautomationcore/uia_private.h | 28 ++- dlls/uiautomationcore/uia_provider.c | 4 +- 5 files changed, 231 insertions(+), 104 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 85270ca7f27..54d6fa710b5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -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 } };
diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 2fb0e88086b..5877d14b8e5 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -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(); } diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index b8c94b831e6..17dd5ab9b6e 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -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 finalize_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 = finalize_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 = finalize_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 = finalize_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; } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 40589d538be..daa6df63044 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -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; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 44850a55b05..99fdd07f40c 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -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))