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 | 68 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 47 +++++++++++++++ dlls/uiautomationcore/uia_event.c | 3 +- dlls/uiautomationcore/uia_private.h | 8 +++ 6 files changed, 133 insertions(+), 10 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b4c920bac71..b6f8b3d75bb 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..ed1a1aa3f04 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([in]long flags, [out, retval]VARIANT *ret_val); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 88e607b6e6a..a7ea3998ff6 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, LONG flags, 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, flags, ret_val); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1972,6 +1988,26 @@ static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, return hr; }
+static HRESULT WINAPI uia_provider_create_node_from_prov(IWineUiaProvider *iface, LONG flags, VARIANT *ret_val) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IRawElementProviderSimple *elprov; + HRESULT hr; + + TRACE("%p, %#lx, %p\n", iface, flags, ret_val); + + if (flags & PROV_METHOD_FLAG_RETURN_NODE_LRES) + FIXME("PROV_METHOD_FLAG_RETURN_NODE_LRES ignored for normal providers.\n"); + + 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 +2019,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, @@ -2431,6 +2468,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, LONG flags, VARIANT *ret_val) +{ + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + HUIANODE node; + HRESULT hr; + VARIANT v; + + TRACE("%p, %#lx, %p\n", iface, flags, ret_val); + + VariantInit(ret_val); + hr = create_node_from_node_provider(prov->nested_node, 0, 0, &v); + if (FAILED(hr) || V_VT(&v) == VT_EMPTY) + return hr; + + if (flags & PROV_METHOD_FLAG_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 +2509,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) diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 91013dfc4ed..e2fd8f5b5b5 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -979,6 +979,40 @@ 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); + + hr = create_node_from_node_provider(&node->IWineUiaNode_iface, 0, PROV_METHOD_FLAG_RETURN_NODE_LRES, &v); + 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 +1032,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 +1045,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..b7b379cdb87 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) { @@ -209,10 +210,15 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c return TRUE; }
+enum provider_method_flags { + PROV_METHOD_FLAG_RETURN_NODE_LRES = 0x0001, +}; + /* uia_client.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, LONG flags, 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; @@ -231,6 +237,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,