From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 16 ++-- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 94 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 49 +++++++++++ dlls/uiautomationcore/uia_event.c | 3 +- dlls/uiautomationcore/uia_private.h | 5 ++ 6 files changed, 158 insertions(+), 10 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f94075a4c81..d00222ad1e1 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -15659,11 +15659,11 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma
set_uia_hwnd_expects(0, 2, 2, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); + ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(uia_com_event_callback); - todo_wine CHECK_CALLED(uia_event_callback); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 3, FALSE, 0, FALSE); + CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_event_callback);
/* * Child ID is ignored when translating EVENT_OBJECT_FOCUS events into @@ -15674,11 +15674,11 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma set_uia_hwnd_expects(0, 2, 2, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, -1); - todo_wine ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); + ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(uia_com_event_callback); - todo_wine CHECK_CALLED(uia_event_callback); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 3, FALSE, 0, FALSE); + CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_event_callback);
/* * UIA queries the serverside provider for UIA_HasKeyboardFocusPropertyId. diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 22d9d650502..a24ce392063 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -95,6 +95,7 @@ library UIA_wine_private HRESULT attach_event([in]LONG_PTR huiaevent); HRESULT respond_to_win_event([in]DWORD win_event, [in]ULONG hwnd, [in]LONG obj_id, [in]LONG child_id, [in]IProxyProviderWinEventSink *sink); + HRESULT create_node_from_prov([out, retval]VARIANT *ret_val); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 88e607b6e6a..5e9c51f550f 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -394,6 +394,22 @@ HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD return hr; }
+HRESULT create_node_from_node_provider(IWineUiaNode *node, int idx, VARIANT *ret_val) +{ + IWineUiaProvider *prov; + HRESULT hr; + + VariantInit(ret_val); + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_create_node_from_prov(prov, ret_val); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1972,6 +1988,23 @@ static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, return hr; }
+static HRESULT WINAPI uia_provider_create_node_from_prov(IWineUiaProvider *iface, VARIANT *ret_val) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IRawElementProviderSimple *elprov; + HRESULT hr; + + TRACE("%p, %p\n", iface, ret_val); + + VariantInit(ret_val); + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderSimple, (void **)&elprov); + if (FAILED(hr)) + return hr; + + /* get_variant_for_elprov_node will release our provider upon failure. */ + return get_variant_for_elprov_node(elprov, prov->return_nested_node, prov->refuse_hwnd_node_providers, ret_val); +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1983,6 +2016,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_get_focus, uia_provider_attach_event, uia_provider_respond_to_win_event, + uia_provider_create_node_from_prov, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2250,6 +2284,7 @@ struct uia_nested_node_provider { LONG ref;
IWineUiaNode *nested_node; + BOOL return_node_lres; };
static inline struct uia_nested_node_provider *impl_from_nested_node_IWineUiaProvider(IWineUiaProvider *iface) @@ -2320,6 +2355,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *if { HUIANODE node;
+ if (prov->return_node_lres) + { + *ret_val = v; + break; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2369,6 +2410,12 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
+ if (prov->return_node_lres) + { + *out_val = v; + return S_OK; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2393,6 +2440,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
+ if (prov->return_node_lres) + { + *out_val = v; + return S_OK; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2431,6 +2484,36 @@ static HRESULT WINAPI uia_nested_node_provider_respond_to_win_event(IWineUiaProv return E_FAIL; }
+static HRESULT WINAPI uia_nested_node_provider_create_node_from_prov(IWineUiaProvider *iface, VARIANT *ret_val) +{ + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + HUIANODE node; + HRESULT hr; + VARIANT v; + + TRACE("%p, %p\n", iface, ret_val); + + VariantInit(ret_val); + hr = create_node_from_node_provider(prov->nested_node, 0, &v); + if (FAILED(hr) || V_VT(&v) == VT_EMPTY) + return hr; + + if (prov->return_node_lres) + { + *ret_val = v; + return S_OK; + } + + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + if (FAILED(hr)) + return hr; + + get_variant_for_node(node, ret_val); + VariantClear(&v); + + return S_OK; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2442,6 +2525,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_get_focus, uia_nested_node_provider_attach_event, uia_nested_node_provider_respond_to_win_event, + uia_nested_node_provider_create_node_from_prov, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) @@ -2452,6 +2536,16 @@ static BOOL is_nested_node_provider(IWineUiaProvider *iface) return FALSE; }
+void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) +{ + struct uia_nested_node_provider *prov; + + assert(is_nested_node_provider(nested_prov)); + + prov = impl_from_nested_node_IWineUiaProvider(nested_prov); + prov->return_node_lres = return_node_lres; +} + static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, BOOL unwrap) { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 91013dfc4ed..e67d98838e3 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -979,6 +979,42 @@ struct uia_com_event { struct uia_event_handler_map_entry *handler_map; };
+static HRESULT uia_com_focus_win_event_callback(struct uia_event *event, void *user_data) +{ + struct uia_node *node = impl_from_IWineUiaNode((IWineUiaNode *)user_data); + VARIANT v, v2; + HRESULT hr; + + /* Only match desktop events. */ + if (!event->desktop_subtree_event) + return S_OK; + + VariantInit(&v); + VariantInit(&v2); + + set_nested_node_prov_return_node_lres(node->prov[get_node_provider_type_at_idx(node, 0)], TRUE); + hr = create_node_from_node_provider(&node->IWineUiaNode_iface, 0, &v); + set_nested_node_prov_return_node_lres(node->prov[get_node_provider_type_at_idx(node, 0)], FALSE); + if (FAILED(hr)) + { + WARN("Failed to create new node lres with hr %#lx\n", hr); + return hr; + } + + if (V_VT(&v) == VT_I4) + { + hr = IWineUiaEvent_raise_event(&event->IWineUiaEvent_iface, v, v2); + if (FAILED(hr)) + { + WARN("raise_event failed with hr %#lx\n", hr); + uia_node_lresult_release(V_I4(&v)); + } + } + VariantClear(&v); + + return hr; +} + static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIANODE node, HWND hwnd) { HRESULT hr; @@ -998,6 +1034,8 @@ static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIAN static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia_event_handler_event_id_map_entry *event_id_map) { struct uia_event_handler_map_entry *entry; + HRESULT hr; + VARIANT v;
LIST_FOR_EACH_ENTRY(entry, &event_id_map->handlers_list, struct uia_event_handler_map_entry, handler_event_id_map_list_entry) { @@ -1009,6 +1047,17 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia uia_com_focus_handler_advise_node(event, node, hwnd); } } + + VariantInit(&v); + hr = UiaGetPropertyValue(node, UIA_HasKeyboardFocusPropertyId, &v); + if (SUCCEEDED(hr) && (V_VT(&v) == VT_BOOL && V_BOOL(&v) == VARIANT_TRUE)) + { + hr = uia_event_for_each(UIA_AutomationFocusChangedEventId, uia_com_focus_win_event_callback, (void *)node, TRUE); + if (FAILED(hr)) + WARN("uia_event_for_each failed with hr %#lx\n", hr); + } + + VariantClear(&v); }
HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, DWORD thread_id, DWORD event_time) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index a82af6b495c..e85bcf2e825 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -252,8 +252,7 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) } }
-typedef HRESULT UiaWineEventForEachCallback(struct uia_event *, void *); -static HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data, +HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data, BOOL clientside_only) { struct uia_event_map_entry *event_entry; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 49d42cbeb45..8b49f3e89ae 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -158,6 +158,7 @@ struct uia_event };
typedef HRESULT UiaWineEventCallback(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); +typedef HRESULT UiaWineEventForEachCallback(struct uia_event *, void *);
static inline void variant_init_bool(VARIANT *v, BOOL val) { @@ -213,11 +214,13 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, LONG child_id, IProxyProviderWinEventSink *sink) DECLSPEC_HIDDEN; +HRESULT create_node_from_node_provider(IWineUiaNode *node, int idx, VARIANT *ret_val) DECLSPEC_HIDDEN; HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers, int node_flags) DECLSPEC_HIDDEN; +void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) DECLSPEC_HIDDEN; HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) DECLSPEC_HIDDEN; void uia_node_lresult_release(LRESULT lr) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_hwnd(HWND hwnd, HUIANODE *out_node, int node_flags) DECLSPEC_HIDDEN; @@ -231,6 +234,8 @@ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPEC_HIDDEN; +HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data, + BOOL clientside_only) DECLSPEC_HIDDEN; BOOL uia_clientside_event_start_event_thread(struct uia_event *event) DECLSPEC_HIDDEN; HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_id, LONG event_cookie) DECLSPEC_HIDDEN; HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events,