From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_event.c | 141 ++++++++++++++++++------------ 1 file changed, 85 insertions(+), 56 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 96b6ac448aa..d493ab0a243 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -252,6 +252,51 @@ 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, + BOOL clientside_only) +{ + struct uia_event_map_entry *event_entry; + HRESULT hr = S_OK; + int i; + + EnterCriticalSection(&event_map_cs); + if ((event_entry = uia_get_event_map_entry_for_event(event_id))) + InterlockedIncrement(&event_entry->refs); + LeaveCriticalSection(&event_map_cs); + + if (!event_entry) + return S_OK; + + for (i = 0; i < 2; i++) + { + struct list *events = !i ? &event_entry->events_list : &event_entry->serverside_events_list; + struct list *cursor, *cursor2; + + if (i && clientside_only) + break; + + LIST_FOR_EACH_SAFE(cursor, cursor2, events) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + /* Event is no longer valid. */ + if (InterlockedCompareExchange(&event->event_defunct, 0, 0) != 0) + continue; + + hr = callback(event, user_data); + if (FAILED(hr)) + goto exit; + } + } + +exit: + if (FAILED(hr)) + WARN("Event callback failed with hr %#lx\n", hr); + uia_event_map_entry_release(event_entry); + return hr; +} + /* * Functions for struct uia_event_args, a reference counted structure * used to store event arguments. This is necessary for serverside events @@ -1525,73 +1570,57 @@ static HRESULT uia_event_check_match(HUIANODE node, HUIANODE nav_start_node, SAF return hr; }
-static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct uia_event_args *args) +struct uia_elprov_event_data { - struct uia_event_map_entry *event_entry; - enum ProviderOptions prov_opts = 0; - struct list *cursor, *cursor2; - HUIANODE node; - SAFEARRAY *sa; - HRESULT hr; - - hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); - if (FAILED(hr)) - return hr; + IRawElementProviderSimple *elprov; + struct uia_event_args *args; + BOOL clientside_only;
- EnterCriticalSection(&event_map_cs); - if ((event_entry = uia_get_event_map_entry_for_event(args->simple_args.EventId))) - InterlockedIncrement(&event_entry->refs); - LeaveCriticalSection(&event_map_cs); + SAFEARRAY *rt_id; + HUIANODE node; +};
- if (!event_entry) - return S_OK; +static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data) +{ + struct uia_elprov_event_data *event_data = (struct uia_elprov_event_data *)data; + HRESULT hr;
- /* - * For events raised on server-side providers, we don't want to add any - * clientside HWND providers. - */ - if (prov_opts & ProviderOptions_ServerSideProvider) - hr = create_uia_node_from_elprov(elprov, &node, FALSE); - else - hr = create_uia_node_from_elprov(elprov, &node, TRUE); - if (FAILED(hr)) + if (!event_data->node) { - uia_event_map_entry_release(event_entry); - return hr; - } + /* + * For events raised on server-side providers, we don't want to add any + * clientside HWND providers. + */ + hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only); + if (FAILED(hr)) + return hr;
- hr = UiaGetRuntimeId(node, &sa); - if (FAILED(hr)) - { - uia_event_map_entry_release(event_entry); - UiaNodeRelease(node); - return hr; + hr = UiaGetRuntimeId(event_data->node, &event_data->rt_id); + if (FAILED(hr)) + return hr; }
- LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->events_list) - { - struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + return uia_event_check_match(event_data->node, event_data->node, event_data->rt_id, event_data->args, event); +}
- hr = uia_event_check_match(node, node, sa, args, event); - if (FAILED(hr)) - break; - } +static HRESULT uia_raise_elprov_event(IRawElementProviderSimple *elprov, struct uia_event_args *args) +{ + struct uia_elprov_event_data event_data = { elprov, args }; + enum ProviderOptions prov_opts = 0; + HRESULT hr;
- if (prov_opts & ProviderOptions_ServerSideProvider) - { - LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->serverside_events_list) - { - struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); + if (FAILED(hr)) + return hr;
- hr = uia_event_check_match(node, node, sa, args, event); - if (FAILED(hr)) - break; - } - } + event_data.clientside_only = !(prov_opts & ProviderOptions_ServerSideProvider); + hr = uia_event_for_each(args->simple_args.EventId, uia_raise_elprov_event_callback, (void *)&event_data, + event_data.clientside_only); + if (FAILED(hr)) + WARN("uia_event_for_each failed with hr %#lx\n", hr);
- uia_event_map_entry_release(event_entry); - SafeArrayDestroy(sa); - UiaNodeRelease(node); + UiaNodeRelease(event_data.node); + SafeArrayDestroy(event_data.rt_id);
return hr; } @@ -1624,7 +1653,7 @@ HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *elprov, EVENTI if (!args) return E_OUTOFMEMORY;
- hr = uia_raise_event(elprov, args); + hr = uia_raise_elprov_event(elprov, args); uia_event_args_release(args); if (FAILED(hr)) return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 12 ++++---- dlls/uiautomationcore/uia_event.c | 33 ++++++++++++++++++++++ dlls/uiautomationcore/uia_private.h | 2 ++ dlls/uiautomationcore/uia_provider.c | 11 ++------ dlls/uiautomationcore/uia_utils.c | 7 ++++- 5 files changed, 50 insertions(+), 15 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 98990d3b19c..01d9448fdfc 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16823,7 +16823,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 2); /* Only called twice on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, FALSE, FALSE, FALSE); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
/* * Get rid of our serverside provider and raise EVENT_OBJECT_FOCUS @@ -16848,7 +16848,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para 1, TRUE, FALSE, TRUE); if (CALLED_COUNT(winproc_GETOBJECT_CLIENT)) ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); method_sequences_enabled = FALSE;
/* @@ -16895,7 +16895,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* * Child HWND now has a serverside provider, WinEvent is ignored. @@ -16906,7 +16906,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); /* Only sent 2 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, FALSE, FALSE, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); CHECK_CALLED_AT_MOST(winproc_GETOBJECT_UiaRoot, 1);
/* @@ -16940,7 +16940,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ child_win_prov_root = NULL; @@ -16950,7 +16950,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 8); /* Only sent 8 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); - todo_wine CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); + CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 2, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index d493ab0a243..ba7e6111241 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -511,6 +511,30 @@ static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event) return hr; }
+static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) +{ + struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + + if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) + { + /* Top level HWND that isn't within our WinEvent HWND scope. */ + if (uia_is_top_level_hwnd(win_event->hwnd)) + return S_OK; + + /* This isn't a top-level HWND, check if its parent HWND is within our scope. */ + if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, GetAncestor(win_event->hwnd, GA_PARENT))) + return S_OK; + } + + /* Has a native serverside provider, no need to do WinEvent translation. */ + if (UiaHasServerSideProvider(win_event->hwnd)) + return S_OK; + + FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n"); + + return S_OK; +} + static void uia_event_thread_process_queue(struct list *event_queue) { while (1) @@ -538,6 +562,15 @@ static void uia_event_thread_process_queue(struct list *event_queue) break; }
+ case QUEUE_EVENT_TYPE_WIN_EVENT: + { + struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)event; + + hr = uia_event_for_each(win_event_to_uia_event_id(win_event->event_id), uia_win_event_for_each_callback, + (void *)win_event, TRUE); + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index c298967be00..218392ef06c 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -255,6 +255,8 @@ HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *el HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; BOOL uia_hwnd_is_visible(HWND hwnd) DECLSPEC_HIDDEN; +BOOL uia_is_top_level_hwnd(HWND hwnd) DECLSPEC_HIDDEN; +BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; void uia_hwnd_map_init(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 269ce63a2fd..6a3b2d061fe 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1545,11 +1545,6 @@ static HRESULT uia_send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPAR return S_OK; }
-static BOOL is_top_level_hwnd(HWND hwnd) -{ - return GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(); -} - static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) { LONG_PTR style, ex_style; @@ -1576,7 +1571,7 @@ static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) }
/* Non top-level HWNDs are considered panes as well. */ - if (!is_top_level_hwnd(hwnd)) + if (!uia_is_top_level_hwnd(hwnd)) *control_type = UIA_PaneControlTypeId; else *control_type = UIA_WindowControlTypeId; @@ -1816,7 +1811,7 @@ static HRESULT WINAPI base_hwnd_fragment_Navigate(IRawElementProviderFragment *i * Top level owned windows have their owner window as a parent instead * of the desktop window. */ - if (is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER))) + if (uia_is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER))) parent = owner; else parent = GetAncestor(base_hwnd_prov->hwnd, GA_PARENT); @@ -1866,7 +1861,7 @@ static HRESULT WINAPI base_hwnd_fragment_get_BoundingRectangle(IRawElementProvid memset(ret_val, 0, sizeof(*ret_val));
/* Top level minimized window - Return empty rect. */ - if (is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd)) + if (uia_is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd)) return S_OK;
if (!GetWindowRect(base_hwnd_prov->hwnd, &rect)) diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c index 9840fe4fe40..866358f97c8 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -404,6 +404,11 @@ BOOL uia_hwnd_is_visible(HWND hwnd) return TRUE; }
+BOOL uia_is_top_level_hwnd(HWND hwnd) +{ + return GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(); +} + /* * rbtree to efficiently store a collection of HWNDs. */ @@ -429,7 +434,7 @@ static void uia_hwnd_map_free(struct rb_entry *entry, void *context) free(hwnd_entry); }
-static BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) { return !!rb_get(hwnd_map, hwnd); }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 42 ++++++++++----- dlls/uiautomationcore/uia_event.c | 62 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 14 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 01d9448fdfc..22c08e9e3fa 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1761,6 +1761,15 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
+#define NODE_CREATE_SEQ_TODO(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_TODO }, \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_TODO }, \ + { prov , FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + #define NODE_CREATE_SEQ_OPTIONAL(prov) \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ /* Win10v1507 and below call this. */ \ @@ -16708,11 +16717,17 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - NODE_CREATE_SEQ(&Provider_child), - { &Provider_child, FRAG_GET_RUNTIME_ID }, - { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + /* + * The following two are currently only done on Wine. Windows doesn't do + * this because the node created is never passed out of the event thread. + */ + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + NODE_CREATE_SEQ_TODO(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 } };
@@ -16846,9 +16861,8 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - if (CALLED_COUNT(winproc_GETOBJECT_CLIENT)) - ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); + ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); + check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE); method_sequences_enabled = FALSE;
/* @@ -16894,7 +16908,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Only sent 4 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* @@ -16940,7 +16954,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ child_win_prov_root = NULL; @@ -16951,7 +16965,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); - check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 2, TRUE, 0, FALSE); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -16993,7 +17007,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 1, 1, 0, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
/* * Top-level HWND, a child of the desktop HWND. Will not have an event @@ -17034,14 +17048,14 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 2, 1); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 2, TRUE, 1, TRUE); + check_uia_hwnd_expects(1, TRUE, 1, FALSE, 1, FALSE, 2, FALSE, 1, FALSE);
/* Child HWNDs of our test window are handled as well. */ SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); set_uia_hwnd_expects(0, 1, 1, 1, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index ba7e6111241..735b112424f 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -511,9 +511,57 @@ static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event) return hr; }
+static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov) +{ + IRawElementProviderSimple *elprov; + IAccessible *acc; + int child_id; + HRESULT hr; + + *ret_elprov = NULL; + hr = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc); + if (FAILED(hr)) + return hr; + + child_id = in_child_id; + if (in_child_id != CHILDID_SELF) + { + IDispatch *disp; + VARIANT cid; + + disp = NULL; + variant_init_i4(&cid, in_child_id); + hr = IAccessible_get_accChild(acc, cid, &disp); + if (FAILED(hr)) + TRACE("get_accChild failed with %#lx!\n", hr); + + if (SUCCEEDED(hr) && disp) + { + IAccessible_Release(acc); + hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc); + IDispatch_Release(disp); + if (FAILED(hr)) + return hr; + + child_id = CHILDID_SELF; + } + } + + hr = create_msaa_provider(acc, child_id, hwnd, in_child_id == CHILDID_SELF, &elprov); + IAccessible_Release(acc); + if (FAILED(hr)) + return hr; + + *ret_elprov = elprov; + return S_OK; +} + static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + IRawElementProviderSimple *elprov; + HUIANODE node; + HRESULT hr;
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) { @@ -530,8 +578,22 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (UiaHasServerSideProvider(win_event->hwnd)) return S_OK;
+ /* + * Regardless of the object ID of the WinEvent, OBJID_CLIENT is queried + * for the HWND with the same child ID as the WinEvent. + */ + hr = create_msaa_provider_from_hwnd(win_event->hwnd, win_event->child_id, &elprov); + if (FAILED(hr)) + return hr; + + hr = create_uia_node_from_elprov(elprov, &node, TRUE); + IRawElementProviderSimple_Release(elprov); + if (FAILED(hr)) + return hr; + FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n");
+ UiaNodeRelease(node); return S_OK; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 6 ------ dlls/uiautomationcore/uia_client.c | 15 ++++++++++----- dlls/uiautomationcore/uia_event.c | 4 ++-- dlls/uiautomationcore/uia_private.h | 4 +++- dlls/uiautomationcore/uia_provider.c | 4 ++-- 5 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 22c08e9e3fa..a178487e420 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16717,12 +16717,6 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - /* - * The following two are currently only done on Wine. Windows doesn't do - * this because the node created is never passed out of the event thread. - */ - { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, - { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, NODE_CREATE_SEQ_TODO(&Provider_child), diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 09e0d179172..36157ee70ba 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -613,6 +613,8 @@ static HRESULT create_uia_node(struct uia_node **out_node, int node_flags) node->ignore_clientside_hwnd_provs = TRUE; if (node_flags & NODE_FLAG_NO_PREPARE) node->no_prepare = TRUE; + if (node_flags & NODE_FLAG_IGNORE_COM_THREADING) + node->ignore_com_threading = TRUE;
*out_node = node; return S_OK; @@ -664,6 +666,9 @@ static HRESULT prepare_uia_node(struct uia_node *node) prov_idx++; }
+ if (node->ignore_com_threading) + return S_OK; + for (i = 0; i < PROV_TYPE_COUNT; i++) { enum ProviderOptions prov_opts; @@ -1361,7 +1366,7 @@ static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BO
VariantInit(v);
- hr = create_uia_node_from_elprov(elprov, &node, !refuse_hwnd_providers); + hr = create_uia_node_from_elprov(elprov, &node, !refuse_hwnd_providers, 0); IRawElementProviderSimple_Release(elprov); if (SUCCEEDED(hr)) { @@ -1910,7 +1915,7 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT if (FAILED(hr)) goto exit;
- hr = create_uia_node_from_elprov(elprov, &node, !prov->refuse_hwnd_node_providers); + hr = create_uia_node_from_elprov(elprov, &node, !prov->refuse_hwnd_node_providers, 0); IRawElementProviderSimple_Release(elprov); if (SUCCEEDED(hr)) { @@ -1969,7 +1974,7 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid
static HRESULT uia_get_providers_for_hwnd(struct uia_node *node); HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, - BOOL get_hwnd_providers) + BOOL get_hwnd_providers, int node_flags) { static const int unsupported_prov_opts = ProviderOptions_ProviderOwnsSetFocus | ProviderOptions_HasNativeIAccessible | ProviderOptions_UseClientCoordinates; @@ -1998,7 +2003,7 @@ HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE else prov_type = PROV_TYPE_MAIN;
- hr = create_uia_node(&node, 0); + hr = create_uia_node(&node, node_flags); if (FAILED(hr)) return hr;
@@ -2040,7 +2045,7 @@ HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE * if (!elprov || !huianode) return E_INVALIDARG;
- return create_uia_node_from_elprov(elprov, huianode, TRUE); + return create_uia_node_from_elprov(elprov, huianode, TRUE, 0); }
/* diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 735b112424f..4d7454ef9ea 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -586,7 +586,7 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
- hr = create_uia_node_from_elprov(elprov, &node, TRUE); + hr = create_uia_node_from_elprov(elprov, &node, TRUE, NODE_FLAG_IGNORE_COM_THREADING); IRawElementProviderSimple_Release(elprov); if (FAILED(hr)) return hr; @@ -1686,7 +1686,7 @@ static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *da * For events raised on server-side providers, we don't want to add any * clientside HWND providers. */ - hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only); + hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only, 0); if (FAILED(hr)) return hr;
diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 218392ef06c..7ac2335dd77 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -57,6 +57,7 @@ enum uia_node_prov_type { enum uia_node_flags { NODE_FLAG_IGNORE_CLIENTSIDE_HWND_PROVS = 0x01, NODE_FLAG_NO_PREPARE = 0x02, + NODE_FLAG_IGNORE_COM_THREADING = 0x04, };
struct uia_node { @@ -74,6 +75,7 @@ struct uia_node { BOOL nested_node; BOOL disconnected; int creator_prov_type; + BOOL ignore_com_threading; BOOL ignore_clientside_hwnd_provs;
struct list prov_thread_list_entry; @@ -212,7 +214,7 @@ HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPE 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) DECLSPEC_HIDDEN; + BOOL get_hwnd_providers, int node_flags) DECLSPEC_HIDDEN; HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) DECLSPEC_HIDDEN; HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) DECLSPEC_HIDDEN; BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 6a3b2d061fe..22eba343511 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -2275,7 +2275,7 @@ LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wparam, return 0; }
- hr = create_uia_node_from_elprov(elprov, &node, FALSE); + hr = create_uia_node_from_elprov(elprov, &node, FALSE, 0); if (FAILED(hr)) { WARN("Failed to create HUIANODE with hr %#lx\n", hr); @@ -2296,7 +2296,7 @@ HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov)
TRACE("(%p)\n", elprov);
- hr = create_uia_node_from_elprov(elprov, &node, FALSE); + hr = create_uia_node_from_elprov(elprov, &node, FALSE, 0); if (FAILED(hr)) return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 2 +- dlls/uiautomationcore/uia_event.c | 2 +- dlls/uiautomationcore/uia_private.h | 4 ++-- dlls/uiautomationcore/uia_provider.c | 25 ++++++++++++++----------- 4 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 36157ee70ba..1ff3e1a9a46 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3013,7 +3013,7 @@ static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderT if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, CHILDID_SELF, hwnd, TRUE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, hwnd, TRUE, TRUE, &elprov); if (FAILED(hr)) WARN("Failed to create MSAA proxy provider with hr %#lx\n", hr);
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 4d7454ef9ea..ddb8d1d56b6 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -547,7 +547,7 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl } }
- hr = create_msaa_provider(acc, child_id, hwnd, in_child_id == CHILDID_SELF, &elprov); + hr = create_msaa_provider(acc, child_id, hwnd, TRUE, in_child_id == CHILDID_SELF, &elprov); IAccessible_Release(acc); if (FAILED(hr)) return hr; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 7ac2335dd77..abd03ce308e 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -243,8 +243,8 @@ HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; -HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL known_root_acc, - IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN; +HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL root_acc_known, + BOOL is_root_acc, IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN;
/* uia_utils.c */ HRESULT register_interface_in_git(IUnknown *iface, REFIID riid, DWORD *ret_cookie) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 22eba343511..917d21d8a82 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -855,7 +855,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, else acc = msaa_prov->acc;
- hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -886,7 +886,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -936,7 +936,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -1031,7 +1031,7 @@ static HRESULT WINAPI msaa_fragment_get_FragmentRoot(IRawElementProviderFragment if (FAILED(hr) || !acc) return hr;
- hr = create_msaa_provider(acc, CHILDID_SELF, msaa_prov->hwnd, TRUE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, msaa_prov->hwnd, TRUE, TRUE, &elprov); IAccessible_Release(acc); if (FAILED(hr)) return hr; @@ -1154,7 +1154,7 @@ static HRESULT msaa_acc_get_focus(struct msaa_provider *prov, struct msaa_provid } }
- hr = create_msaa_provider(focus_acc, focus_cid, hwnd, FALSE, &elprov); + hr = create_msaa_provider(focus_acc, focus_cid, hwnd, FALSE, FALSE, &elprov); IAccessible_Release(focus_acc); if (SUCCEEDED(hr)) *out_prov = impl_from_msaa_provider(elprov); @@ -1176,7 +1176,7 @@ static HRESULT WINAPI msaa_fragment_root_GetFocus(IRawElementProviderFragmentRoo if (V_I4(&msaa_prov->cid) != CHILDID_SELF) return S_OK;
- hr = create_msaa_provider(msaa_prov->acc, CHILDID_SELF, msaa_prov->hwnd, FALSE, &elprov); + hr = create_msaa_provider(msaa_prov->acc, CHILDID_SELF, msaa_prov->hwnd, FALSE, FALSE, &elprov); if (FAILED(hr)) return hr;
@@ -1440,8 +1440,8 @@ static const IProxyProviderWinEventHandlerVtbl msaa_winevent_handler_vtbl = { msaa_winevent_handler_RespondToWinEvent, };
-HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL known_root_acc, - IRawElementProviderSimple **elprov) +HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL root_acc_known, + BOOL is_root_acc, IRawElementProviderSimple **elprov) { struct msaa_provider *msaa_prov = calloc(1, sizeof(*msaa_prov));
@@ -1470,8 +1470,11 @@ HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL kn else msaa_prov->hwnd = hwnd;
- if (known_root_acc) - msaa_prov->root_acc_check_ran = msaa_prov->is_root_acc = TRUE; + if (root_acc_known) + { + msaa_prov->root_acc_check_ran = TRUE; + msaa_prov->is_root_acc = is_root_acc; + }
*elprov = &msaa_prov->IRawElementProviderSimple_iface;
@@ -1515,7 +1518,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, LONG child_id, DWORD if (!hwnd) return E_FAIL;
- return create_msaa_provider(acc, child_id, hwnd, FALSE, elprov); + return create_msaa_provider(acc, child_id, hwnd, FALSE, FALSE, elprov); }
static HRESULT uia_get_hr_for_last_error(void)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 4 +- dlls/uiautomationcore/uia_classes.idl | 2 + dlls/uiautomationcore/uia_client.c | 46 +++++++++++++++ dlls/uiautomationcore/uia_event.c | 67 +++++++++++++++++++++- dlls/uiautomationcore/uia_private.h | 4 ++ 5 files changed, 119 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a178487e420..f7afe254aed 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16717,8 +16717,8 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, - { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, + { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, NODE_CREATE_SEQ_TODO(&Provider_child), { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index b1dea2d414d..33284a252c9 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -92,6 +92,8 @@ library UIA_wine_private HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val); HRESULT get_focus([out, retval]VARIANT *ret_val); 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]LONG_PTR huiaevent); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1ff3e1a9a46..f50c739ae97 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -378,6 +378,22 @@ static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEV return hr; }
+HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, + LONG child_id, struct uia_event *event) +{ + IWineUiaProvider *prov; + HRESULT hr; + + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_respond_to_win_event(prov, win_event, HandleToUlong(hwnd), obj_id, child_id, (LONG_PTR)event); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1939,6 +1955,25 @@ exit: return hr; }
+static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, LONG obj_id, + LONG child_id, LONG_PTR huiaevent) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + struct uia_event *event = (struct uia_event *)huiaevent; + IProxyProviderWinEventHandler *handler; + HRESULT hr; + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IProxyProviderWinEventHandler, (void **)&handler); + if (FAILED(hr)) + return S_OK; + + hr = IProxyProviderWinEventHandler_RespondToWinEvent(handler, win_event, UlongToHandle(hwnd), obj_id, child_id, + &event->u.clientside.IProxyProviderWinEventSink_iface); + IProxyProviderWinEventHandler_Release(handler); + + return hr; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1949,6 +1984,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_navigate, uia_provider_get_focus, uia_provider_attach_event, + uia_provider_respond_to_win_event, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2388,6 +2424,15 @@ static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *if return hr; }
+static HRESULT WINAPI uia_nested_node_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, + LONG obj_id, LONG child_id, LONG_PTR huiaevent) +{ + FIXME("%p, %#lx, #%lx, %#lx, %#lx, %#Ix: stub\n", iface, win_event, hwnd, obj_id, child_id, huiaevent); + /* This should not be called. */ + assert(0); + return E_FAIL; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2398,6 +2443,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_navigate, uia_nested_node_provider_get_focus, uia_nested_node_provider_attach_event, + uia_nested_node_provider_respond_to_win_event, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index ddb8d1d56b6..97171e7bebc 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -20,7 +20,6 @@
#include "wine/debug.h" #include "wine/rbtree.h" -#include "assert.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
@@ -560,8 +559,10 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; IRawElementProviderSimple *elprov; + struct uia_node *node_data; HUIANODE node; HRESULT hr; + int i;
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) { @@ -591,7 +592,14 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
- FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n"); + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + for (i = 0; i < node_data->prov_count; i++) + { + hr = respond_to_win_event_on_node_provider((IWineUiaNode *)node, i, win_event->event_id, win_event->hwnd, win_event->obj_id, + win_event->child_id, event); + if (FAILED(hr)) + break; + }
UiaNodeRelease(node); return S_OK; @@ -993,6 +1001,60 @@ static HRESULT create_uia_event(struct uia_event **out_event, LONG event_cookie, return S_OK; }
+/* + * IProxyProviderWinEventSink interface implementation for clientside events. + */ +static HRESULT WINAPI ProxyEventSink_QueryInterface(IProxyProviderWinEventSink *iface, REFIID riid, void **obj) +{ + *obj = NULL; + if (IsEqualIID(riid, &IID_IProxyProviderWinEventSink) || IsEqualIID(riid, &IID_IUnknown)) + *obj = iface; + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI ProxyEventSink_AddRef(IProxyProviderWinEventSink *iface) +{ + return 2; +} + +static ULONG WINAPI ProxyEventSink_Release(IProxyProviderWinEventSink *iface) +{ + return 1; +} + +static HRESULT WINAPI ProxyEventSink_AddAutomationPropertyChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, PROPERTYID prop_id, VARIANT new_value) +{ + FIXME("%p, %p, %d, %s: stub\n", iface, elprov, prop_id, debugstr_variant(&new_value)); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProxyEventSink_AddAutomationEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, EVENTID event_id) +{ + FIXME("%p, %p, %d: stub\n", iface, elprov, event_id); + return S_OK; +} + +static HRESULT WINAPI ProxyEventSink_AddStructureChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, enum StructureChangeType structure_change_type, SAFEARRAY *runtime_id) +{ + FIXME("%p, %p, %d, %p: stub\n", iface, elprov, structure_change_type, runtime_id); + return E_NOTIMPL; +} + +static const IProxyProviderWinEventSinkVtbl proxy_event_sink_vtbl = { + ProxyEventSink_QueryInterface, + ProxyEventSink_AddRef, + ProxyEventSink_Release, + ProxyEventSink_AddAutomationPropertyChangedEvent, + ProxyEventSink_AddAutomationEvent, + ProxyEventSink_AddStructureChangedEvent, +}; + static HRESULT create_clientside_uia_event(struct uia_event **out_event, int event_id, int scope, UiaWineEventCallback *cback, void *cback_data, SAFEARRAY *runtime_id) { @@ -1011,6 +1073,7 @@ static HRESULT create_clientside_uia_event(struct uia_event **out_event, int eve event->u.clientside.event_callback = cback; event->u.clientside.callback_data = cback_data; uia_hwnd_map_init(&event->u.clientside.win_event_hwnd_map); + event->u.clientside.IProxyProviderWinEventSink_iface.lpVtbl = &proxy_event_sink_vtbl;
*out_event = event; return S_OK; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index abd03ce308e..de6e547dfa4 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -22,6 +22,7 @@ #include "uia_classes.h" #include "wine/list.h" #include "wine/rbtree.h" +#include "assert.h"
extern HMODULE huia_module DECLSPEC_HIDDEN;
@@ -143,6 +144,7 @@ struct uia_event HRESULT (*event_callback)(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); void *callback_data;
+ IProxyProviderWinEventSink IProxyProviderWinEventSink_iface; struct rb_tree win_event_hwnd_map; BOOL event_thread_started; DWORD git_cookie; @@ -210,6 +212,8 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c
/* 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, struct uia_event *event) 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;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 31 +++----- dlls/uiautomationcore/uia_client.c | 1 - dlls/uiautomationcore/uia_event.c | 85 +++++++++++++++++++--- dlls/uiautomationcore/uia_private.h | 2 + 4 files changed, 87 insertions(+), 32 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f7afe254aed..4ad1ad17214 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1761,15 +1761,6 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
-#define NODE_CREATE_SEQ_TODO(prov) \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, \ - /* Win10v1507 and below call this. */ \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ - { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_TODO }, \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_TODO }, \ - { prov , FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ - #define NODE_CREATE_SEQ_OPTIONAL(prov) \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ /* Win10v1507 and below call this. */ \ @@ -16719,9 +16710,9 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - NODE_CREATE_SEQ_TODO(&Provider_child), - { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, - { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ { 0 } };
@@ -16854,7 +16845,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para method_sequences_enabled = TRUE; set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE); method_sequences_enabled = FALSE; @@ -16901,7 +16892,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 1, 1, 2, 0); SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Only sent 4 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
@@ -16947,7 +16938,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para */ set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ @@ -16957,7 +16948,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 2, 2, 4, 0); SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 8); /* Only sent 8 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); + ARRAY_SIZE(event_handles), TRUE, TRUE, FALSE); CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE);
@@ -17000,7 +16991,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para UIA_AutomationFocusChangedEventId); set_uia_hwnd_expects(0, 1, 1, 0, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
/* @@ -17041,14 +17032,14 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para /* WinEvent handled. */ set_uia_hwnd_expects(1, 1, 1, 2, 1); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(1, TRUE, 1, FALSE, 1, FALSE, 2, FALSE, 1, FALSE);
/* Child HWNDs of our test window are handled as well. */ SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); set_uia_hwnd_expects(0, 1, 1, 1, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); @@ -17086,7 +17077,7 @@ skip_win_event_hwnd_filter_test: /* WinEvent handled by default MSAA proxy provider. */ set_uia_hwnd_expects(1, 1, 1, 4, 5); test_uia_event_win_event_mapping(EVENT_SYSTEM_ALERT, hwnd[0], OBJID_CLIENT, 2, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_most(1, 1, 1, 4, 5);
hr = UiaRemoveEvent(event); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index f50c739ae97..1ecb2b436d0 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1890,7 +1890,6 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT if (!prov->return_nested_node && SUCCEEDED(IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IProxyProviderWinEventHandler, (void **)&winevent_handler))) { - FIXME("MSAA to UIA event bridge currently unimplemented\n"); hr = uia_event_add_win_event_hwnd(event, prov->hwnd); if (FAILED(hr)) WARN("Failed to add hwnd for win_event, hr %#lx\n", hr); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 97171e7bebc..f6e3cb430f0 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -555,11 +555,32 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl return S_OK; }
+struct event_sink_event +{ + struct list event_sink_list_entry; + + IRawElementProviderSimple *elprov; + struct uia_event_args *args; +}; + +struct uia_elprov_event_data +{ + IRawElementProviderSimple *elprov; + struct uia_event_args *args; + BOOL clientside_only; + + SAFEARRAY *rt_id; + HUIANODE node; +}; + +static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data); static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + struct event_sink_event *sink_event, *sink_event2; IRawElementProviderSimple *elprov; struct uia_node *node_data; + struct list sink_events; HUIANODE node; HRESULT hr; int i; @@ -592,6 +613,9 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
+ list_init(&sink_events); + event->u.clientside.sink_events = &sink_events; + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); for (i = 0; i < node_data->prov_count; i++) { @@ -599,8 +623,33 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da win_event->child_id, event); if (FAILED(hr)) break; + + if (!list_empty(&sink_events)) + break; + } + + LIST_FOR_EACH_ENTRY_SAFE(sink_event, sink_event2, &sink_events, struct event_sink_event, event_sink_list_entry) + { + list_remove(&sink_event->event_sink_list_entry); + + if (sink_event->args->simple_args.EventId == event->event_id) + { + struct uia_elprov_event_data event_data = { sink_event->elprov, sink_event->args, TRUE }; + + hr = uia_raise_elprov_event_callback(event, (void *)&event_data); + if (FAILED(hr)) + WARN("uia_raise_elprov_event_callback failed with hr %#lx\n", hr); + + UiaNodeRelease(event_data.node); + SafeArrayDestroy(event_data.rt_id); + } + + IRawElementProviderSimple_Release(sink_event->elprov); + uia_event_args_release(sink_event->args); + free(sink_event); }
+ event->u.clientside.sink_events = NULL; UiaNodeRelease(node); return S_OK; } @@ -1004,6 +1053,11 @@ static HRESULT create_uia_event(struct uia_event **out_event, LONG event_cookie, /* * IProxyProviderWinEventSink interface implementation for clientside events. */ +static inline struct uia_event *impl_from_ProxyEventSink(IProxyProviderWinEventSink *iface) +{ + return CONTAINING_RECORD(iface, struct uia_event, u.clientside.IProxyProviderWinEventSink_iface); +} + static HRESULT WINAPI ProxyEventSink_QueryInterface(IProxyProviderWinEventSink *iface, REFIID riid, void **obj) { *obj = NULL; @@ -1035,7 +1089,26 @@ static HRESULT WINAPI ProxyEventSink_AddAutomationPropertyChangedEvent(IProxyPro static HRESULT WINAPI ProxyEventSink_AddAutomationEvent(IProxyProviderWinEventSink *iface, IRawElementProviderSimple *elprov, EVENTID event_id) { - FIXME("%p, %p, %d: stub\n", iface, elprov, event_id); + const struct uia_event_info *event_info = uia_event_info_from_id(event_id); + struct event_sink_event *sink_event = calloc(1, sizeof(*sink_event)); + struct uia_event *event = impl_from_ProxyEventSink(iface); + + TRACE("%p, %p, %d\n", iface, elprov, event_id); + + if (!sink_event) + return E_OUTOFMEMORY; + + sink_event->args = create_uia_event_args(event_info); + if (!sink_event->args) + { + free(sink_event); + return E_OUTOFMEMORY; + } + + IRawElementProviderSimple_AddRef(elprov); + sink_event->elprov = elprov; + list_add_tail(event->u.clientside.sink_events, &sink_event->event_sink_list_entry); + return S_OK; }
@@ -1728,16 +1801,6 @@ static HRESULT uia_event_check_match(HUIANODE node, HUIANODE nav_start_node, SAF return hr; }
-struct uia_elprov_event_data -{ - IRawElementProviderSimple *elprov; - struct uia_event_args *args; - BOOL clientside_only; - - SAFEARRAY *rt_id; - HUIANODE node; -}; - static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data) { struct uia_elprov_event_data *event_data = (struct uia_elprov_event_data *)data; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index de6e547dfa4..7a787d5efef 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -145,6 +145,8 @@ struct uia_event void *callback_data;
IProxyProviderWinEventSink IProxyProviderWinEventSink_iface; + struct list *sink_events; + struct rb_tree win_event_hwnd_map; BOOL event_thread_started; DWORD git_cookie;
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_event.c:
return hr;
}
+static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) +{
- struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data;
- if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd))
- {
/* Top level HWND that isn't within our WinEvent HWND scope. */
if (uia_is_top_level_hwnd(win_event->hwnd))
return S_OK;
/* This isn't a top-level HWND, check if its parent HWND is within our scope. */
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, GetAncestor(win_event->hwnd, GA_PARENT)))
return S_OK;
Is this correct even if the parent is also not a top-level HWND?
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_event.c:
*obj = iface;
- else
return E_NOINTERFACE;
- return S_OK;
+}
+static ULONG WINAPI ProxyEventSink_AddRef(IProxyProviderWinEventSink *iface) +{
- return 2;
+}
+static ULONG WINAPI ProxyEventSink_Release(IProxyProviderWinEventSink *iface) +{
- return 1;
+}
I'm not sure that leaving out refcounting here is safe. It wouldn't make much sense, but I don't see any reason the IProxyProvider couldn't addref and save this object past the event lifetime.
On Tue Sep 26 19:05:48 2023 +0000, Esme Povirk wrote:
I'm not sure that leaving out refcounting here is safe. It wouldn't make much sense, but I don't see any reason the IProxyProvider couldn't addref and save this object past the event lifetime.
Actually I could see a GC'd environment like Mono or .NET incidentally doing that.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_event.c:
- LIST_FOR_EACH_ENTRY_SAFE(sink_event, sink_event2, &sink_events, struct event_sink_event, event_sink_list_entry)
- {
list_remove(&sink_event->event_sink_list_entry);
if (sink_event->args->simple_args.EventId == event->event_id)
{
struct uia_elprov_event_data event_data = { sink_event->elprov, sink_event->args, TRUE };
hr = uia_raise_elprov_event_callback(event, (void *)&event_data);
if (FAILED(hr))
WARN("uia_raise_elprov_event_callback failed with hr %#lx\n", hr);
UiaNodeRelease(event_data.node);
SafeArrayDestroy(event_data.rt_id);
}
This doesn't make a lot of sense to me. Why does the IProxyProviderWinEventSink interface accept an event id if the "wrong" one is just going to be ignored?
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_event.c:
- TRACE("%p, %p, %d\n", iface, elprov, event_id);
- if (!sink_event)
return E_OUTOFMEMORY;
- sink_event->args = create_uia_event_args(event_info);
- if (!sink_event->args)
- {
free(sink_event);
return E_OUTOFMEMORY;
- }
- IRawElementProviderSimple_AddRef(elprov);
- sink_event->elprov = elprov;
- list_add_tail(event->u.clientside.sink_events, &sink_event->event_sink_list_entry);
I think sink_events needs to be checked for NULL.