From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 30 ++++----- dlls/uiautomationcore/uia_com_client.c | 72 +++++++++++++++++++++- dlls/uiautomationcore/uia_event.c | 31 ++++++---- dlls/uiautomationcore/uia_private.h | 3 + 4 files changed, 107 insertions(+), 29 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ae2758c39bf..6cdc19b739d 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -14943,8 +14943,8 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface set_provider_method_event_data(&Provider, NULL, -1); goto exit; } - todo_wine ok(wait_res != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + ok(wait_res != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE);
/* * Manually fire off EVENT_OBJECT_SHOW, providers will be advised of @@ -14952,8 +14952,8 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface */ set_uia_hwnd_expects(0, 2, 2, 6, 0); /* Only done more than one of each on Win11. */ NotifyWinEvent(EVENT_OBJECT_SHOW, test_hwnd, OBJID_WINDOW, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE);
/* * Providers are only advised of events being listened for if an event is @@ -14977,17 +14977,17 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 6); /* Only done more than once on Win11. */ set_uia_hwnd_expects(0, 2, 3, 5, 0); /* Only done more than one of each on Win11. */ ShowWindow(test_child_hwnd, SW_SHOW); - todo_wine ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* Same deal as before, it will advise multiple times. */ SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 6); /* Only done more than once on Win11. */ set_uia_hwnd_expects(0, 2, 3, 5, 0); /* Only done more than one of each on Win11. */ NotifyWinEvent(EVENT_OBJECT_SHOW, test_child_hwnd, OBJID_WINDOW, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + ok(msg_wait_for_all_events(method_event, event_handle_count, 3000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* Break navigation chain, can't reach our test element so no advisement. */ Provider_hwnd3.parent = NULL; @@ -14995,8 +14995,8 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface set_uia_hwnd_expects(0, 1, 1, 1, 0); NotifyWinEvent(EVENT_OBJECT_SHOW, test_child_hwnd, OBJID_WINDOW, CHILDID_SELF); ok(msg_wait_for_all_events(method_event, event_handle_count, 2000) == WAIT_TIMEOUT, "Wait for method_event(s) didn't timeout.\n"); - check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
set_provider_method_event_data(&Provider_hwnd3, NULL, -1); set_provider_method_event_data(&Provider_nc3, NULL, -1); @@ -15044,10 +15044,10 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface SET_EXPECT(child_winproc_GETOBJECT_UiaRoot); /* Only done on Win11. */ set_uia_hwnd_expects(0, 3, 3, 1, 0); /* Only done more than once on Win11. */ NotifyWinEvent(EVENT_OBJECT_SHOW, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(method_event, event_handle_count, 2000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + ok(msg_wait_for_all_events(method_event, event_handle_count, 2000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); CHECK_CALLED_AT_MOST(winproc_GETOBJECT_UiaRoot, 1); CHECK_CALLED_AT_MOST(child_winproc_GETOBJECT_UiaRoot, 1); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
set_provider_method_event_data(&Provider_hwnd, NULL, -1); set_provider_method_event_data(&Provider_nc, NULL, -1); @@ -15063,7 +15063,7 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface SET_EXPECT(child_winproc_GETOBJECT_UiaRoot); /* Only done on Win11. */ set_uia_hwnd_expects(0, 2, 2, 7, 0); /* Only done more than once on Win11. */ NotifyWinEvent(EVENT_OBJECT_SHOW, test_hwnd, OBJID_WINDOW, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(method_event, event_handle_count, 2000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + ok(msg_wait_for_all_events(method_event, event_handle_count, 2000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); CHECK_CALLED_AT_MOST(child_winproc_GETOBJECT_UiaRoot, 1); check_uia_hwnd_expects_at_most(0, 2, 2, 7, 0);
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 8f8d5d0170e..6d3d2216169 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -960,7 +960,77 @@ struct uia_com_event {
HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, DWORD thread_id, DWORD event_time) { - FIXME("%ld, %p, %ld, %ld, %ld, %ld: stub\n", event_id, hwnd, obj_id, child_id, thread_id, event_time); + LONG handler_count; + + TRACE("%ld, %p, %ld, %ld, %ld, %ld\n", event_id, hwnd, obj_id, child_id, thread_id, event_time); + + EnterCriticalSection(&com_event_handlers_cs); + handler_count = com_event_handlers.handler_count; + LeaveCriticalSection(&com_event_handlers_cs); + + if (!handler_count) + return S_OK; + + switch (event_id) + { + case EVENT_OBJECT_SHOW: + { + struct uia_event_handler_map_entry *entry; + SAFEARRAY *rt_id = NULL; + HUIANODE node; + HRESULT hr; + + if (obj_id != OBJID_WINDOW || !uia_hwnd_is_visible(hwnd)) + break; + + hr = UiaNodeFromHandle(hwnd, &node); + if (FAILED(hr)) + return hr; + + hr = UiaGetRuntimeId(node, &rt_id); + if (FAILED(hr)) + { + UiaNodeRelease(node); + return hr; + } + + EnterCriticalSection(&com_event_handlers_cs); + + RB_FOR_EACH_ENTRY(entry, &com_event_handlers.handler_map, struct uia_event_handler_map_entry, entry) + { + struct uia_com_event *event; + + /* + * Focus change event handlers only listen for EVENT_OBJECT_SHOW + * on the desktop HWND. + */ + if ((entry->event_id == UIA_AutomationFocusChangedEventId) && (hwnd != GetDesktopWindow())) + continue; + + LIST_FOR_EACH_ENTRY(event, &entry->handlers_list, struct uia_com_event, event_handler_map_list_entry) + { + hr = uia_event_check_node_within_event_scope((struct uia_event *)event->event, node, rt_id, NULL); + if (FAILED(hr)) + WARN("uia_event_check_node_within_scope failed with hr %#lx\n", hr); + else if (hr == S_OK) + { + hr = uia_event_advise_node((struct uia_event *)event->event, node); + if (FAILED(hr)) + WARN("uia_event_advise_node failed with hr %#lx\n", hr); + } + } + } + + LeaveCriticalSection(&com_event_handlers_cs); + + UiaNodeRelease(node); + break; + } + + default: + break; + } + return S_OK; }
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 7d3af1a9d3c..0e6cf4e000a 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -55,6 +55,7 @@ static int win_event_to_uia_event_id(int win_event) { case EVENT_OBJECT_FOCUS: return UIA_AutomationFocusChangedEventId; case EVENT_SYSTEM_ALERT: return UIA_SystemAlertEventId; + case EVENT_OBJECT_SHOW: return UIA_StructureChangedEventId;
default: break; @@ -1579,13 +1580,27 @@ static HRESULT uia_event_advise(struct uia_event *event, BOOL advise_added, LONG return hr; }
+HRESULT uia_event_advise_node(struct uia_event *event, HUIANODE node) +{ + int old_event_advisers_count = event->event_advisers_count; + HRESULT hr; + + hr = attach_event_to_uia_node(node, event); + if (FAILED(hr)) + return hr; + + if (event->event_advisers_count != old_event_advisers_count) + hr = uia_event_advise(event, TRUE, old_event_advisers_count); + + return hr; +} + /*********************************************************************** * UiaEventAddWindow (uiautomationcore.@) */ HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd) { struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); - int old_event_advisers_count; HUIANODE node; HRESULT hr;
@@ -1600,15 +1615,7 @@ HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd) if (FAILED(hr)) return hr;
- old_event_advisers_count = event->event_advisers_count; - hr = attach_event_to_uia_node(node, event); - if (FAILED(hr)) - goto exit; - - if (event->event_advisers_count != old_event_advisers_count) - hr = uia_event_advise(event, TRUE, old_event_advisers_count); - -exit: + hr = uia_event_advise_node(event, node); UiaNodeRelease(node);
return hr; @@ -1734,8 +1741,6 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) return S_OK; }
-static HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id, - HUIANODE *clientside_nav_node_out); static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, struct uia_event *event) { HRESULT hr = S_OK; @@ -1812,7 +1817,7 @@ static void set_refuse_hwnd_providers(struct uia_node *node, BOOL refuse_hwnd_pr * If it isn't, return S_FALSE. * Upon failure, return a failure HR. */ -static HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id, +HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id, HUIANODE *clientside_nav_node_out) { struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId }; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 358a0c95217..0a8f2d69dbd 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -234,9 +234,12 @@ HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_i HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) DECLSPEC_HIDDEN; HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) DECLSPEC_HIDDEN; +HRESULT uia_event_advise_node(struct uia_event *event, HUIANODE node) DECLSPEC_HIDDEN; HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback, void *cback_data, HUIAEVENT *huiaevent) DECLSPEC_HIDDEN; +HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id, + HUIANODE *clientside_nav_node_out) DECLSPEC_HIDDEN;
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;