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 | 1 + dlls/uiautomationcore/uia_client.c | 14 +- dlls/uiautomationcore/uia_event.c | 206 +++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 12 ++ 5 files changed, 207 insertions(+), 30 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f7f16927b38..788eb1a4eba 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13976,8 +13976,8 @@ static void test_UiaAddEvent_client_proc(void) post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, HandleToUlong(hwnd), PROVIDER_ID, ProviderOptions_ServerSideProvider); todo_wine ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); todo_wine CHECK_CALLED(uia_event_callback); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED(prov_callback_proxy);
/* diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 25417d6c549..8eb9998f945 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -75,6 +75,7 @@ library UIA_wine_private HRESULT advise_events([in]BOOL advise_added, [in]long adviser_start_idx); HRESULT set_event_data([in]const GUID *event_guid, [in]long scope, [in]VARIANT runtime_id, [in]IWineUiaEvent *event_iface); + HRESULT raise_event([in]VARIANT in_node); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 25e76db8128..492ca0e52de 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1266,17 +1266,6 @@ static ULONG WINAPI uia_provider_Release(IWineUiaProvider *iface) return ref; }
-static void get_variant_for_node(HUIANODE node, VARIANT *v) -{ -#ifdef _WIN64 - V_VT(v) = VT_I8; - V_I8(v) = (UINT64)node; -#else - V_VT(v) = VT_I4; - V_I4(v) = (UINT32)node; -#endif -} - static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BOOL out_nested, VARIANT *v) { @@ -2171,7 +2160,6 @@ static ULONG WINAPI uia_nested_node_provider_Release(IWineUiaProvider *iface) return ref; }
-static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode); static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *iface, const struct uia_prop_info *prop_info, VARIANT *ret_val) { @@ -2430,7 +2418,7 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU return S_OK; }
-static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) +HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) { struct uia_node *node; HRESULT hr; diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 242ee992c40..4f5483d4c74 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -79,6 +79,7 @@ struct uia_event_map_entry * ignored. */ struct list events_list; + struct list serverside_events_list; };
struct uia_event_identifier { @@ -146,6 +147,7 @@ static HRESULT uia_event_map_add_event(struct uia_event *event)
event_entry->event_id = event->event_id; list_init(&event_entry->events_list); + list_init(&event_entry->serverside_events_list);
if (!uia_event_map.event_count) rb_init(&uia_event_map.event_map, uia_event_map_id_compare); @@ -154,7 +156,10 @@ static HRESULT uia_event_map_add_event(struct uia_event *event) }
IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); - list_add_head(&event_entry->events_list, &event->event_list_entry); + if (event->event_type == EVENT_TYPE_SERVERSIDE) + list_add_head(&event_entry->serverside_events_list, &event->event_list_entry); + else + list_add_head(&event_entry->events_list, &event->event_list_entry); InterlockedIncrement(&event_entry->refs);
event->event_map_entry = event_entry; @@ -195,6 +200,13 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) IWineUiaEvent_Release(&event->IWineUiaEvent_iface); }
+ LIST_FOR_EACH_SAFE(cursor, cursor2, &entry->serverside_events_list) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + } + heap_free(entry); } } @@ -239,9 +251,12 @@ struct uia_event_thread HANDLE hthread; HWND hwnd; LONG ref; + + struct list *event_queue; };
#define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1) +#define WM_UIA_EVENT_THREAD_PROCESS_QUEUE (WM_USER + 2) static struct uia_event_thread event_thread; static CRITICAL_SECTION event_thread_cs; static CRITICAL_SECTION_DEBUG event_thread_cs_debug = @@ -252,12 +267,108 @@ static CRITICAL_SECTION_DEBUG event_thread_cs_debug = }; static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0, 0 };
+struct uia_queue_event +{ + struct list event_queue_entry; + + struct uia_event_args *args; + struct uia_event *event; + VARIANT node; +}; + +static HRESULT uia_event_queue_post_event(struct uia_event *event, VARIANT node, struct uia_event_args *args) +{ + struct uia_queue_event *queue_event = heap_alloc_zero(sizeof(*queue_event)); + + if (!queue_event) + return E_OUTOFMEMORY; + + IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); + InterlockedIncrement(&args->ref); + queue_event->args = args; + queue_event->event = event; + queue_event->node = node; + + EnterCriticalSection(&event_thread_cs); + + assert(event_thread.event_queue); + list_add_tail(event_thread.event_queue, &queue_event->event_queue_entry); + PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0); + + LeaveCriticalSection(&event_thread_cs); + + return S_OK; +} + +static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue, BOOL *event_thread_shutdown) +{ + struct uia_queue_event *queue_event = NULL; + + if (!*event_thread_shutdown) + { + EnterCriticalSection(&event_thread_cs); + if (event_queue != event_thread.event_queue) + { + *event_thread_shutdown = TRUE; + LeaveCriticalSection(&event_thread_cs); + } + } + + if (!list_empty(event_queue)) + { + queue_event = LIST_ENTRY(list_head(event_queue), struct uia_queue_event, event_queue_entry); + list_remove(list_head(event_queue)); + } + + if (!*event_thread_shutdown) + LeaveCriticalSection(&event_thread_cs); + + return queue_event; +} + +static void uia_event_thread_process_queue(struct list *event_queue) +{ + BOOL event_thread_shutdown = FALSE; + + while (1) + { + struct uia_queue_event *event; + HUIANODE node; + HRESULT hr; + + if (!(event = uia_event_queue_pop(event_queue, &event_thread_shutdown))) + break; + + hr = UiaHUiaNodeFromVariant(&event->node, &node); + if (SUCCEEDED(hr)) + { + LRESULT lr; + VARIANT v; + + if ((lr = uia_lresult_from_node(node))) + { + V_VT(&v) = VT_I4; + V_I4(&v) = lr; + hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v); + if (FAILED(hr)) + WARN("IWineUiaEvent_raise_event failed with hr %#lx\n", hr); + } + } + + uia_event_args_release(event->args); + IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface); + heap_free(event); + } +} + static DWORD WINAPI uia_event_thread_proc(void *arg) { HANDLE initialized_event = arg; + struct list event_queue; HWND hwnd; MSG msg;
+ list_init(&event_queue); CoInitializeEx(NULL, COINIT_MULTITHREADED); hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); if (!hwnd) @@ -268,14 +379,19 @@ static DWORD WINAPI uia_event_thread_proc(void *arg) }
event_thread.hwnd = hwnd; + event_thread.event_queue = &event_queue;
/* Initialization complete, thread can now process window messages. */ SetEvent(initialized_event); TRACE("Event thread started.\n"); while (GetMessageW(&msg, NULL, 0, 0)) { - if (msg.message == WM_UIA_EVENT_THREAD_STOP) - break; + if (msg.message == WM_UIA_EVENT_THREAD_STOP || msg.message == WM_UIA_EVENT_THREAD_PROCESS_QUEUE) + { + uia_event_thread_process_queue(&event_queue); + if (msg.message == WM_UIA_EVENT_THREAD_STOP) + break; + }
TranslateMessage(&msg); DispatchMessageW(&msg); @@ -431,6 +547,17 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ return hr; }
+ /* + * First call to advise events on a serverside provider, add it to the + * events list so it can be raised. + */ + if (!adviser_start_idx && advise_added && event->event_type == EVENT_TYPE_SERVERSIDE) + { + hr = uia_event_map_add_event(event); + if (FAILED(hr)) + WARN("Failed to add event to event map, hr %#lx\n", hr); + } + /* * Once we've advised of removal, no need to keep the advisers around. * We can also release our reference to the event map. @@ -438,9 +565,7 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ if (!advise_added) { InterlockedIncrement(&event->event_defunct); - /* FIXME: Remove this check once we can raise serverside events. */ - if (event->event_type == EVENT_TYPE_CLIENTSIDE) - uia_event_map_entry_release(event->event_map_entry); + uia_event_map_entry_release(event->event_map_entry); event->event_map_entry = NULL;
for (i = 0; i < event->event_advisers_count; i++) @@ -480,12 +605,31 @@ static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID return S_OK; }
+static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + HUIANODE node; + HRESULT hr; + + FIXME("%p, %s: stub\n", iface, debugstr_variant(&in_node)); + + assert(event->event_type != EVENT_TYPE_SERVERSIDE); + + hr = uia_node_from_lresult((LRESULT)V_I4(&in_node), &node); + if (FAILED(hr)) + return hr; + + UiaNodeRelease(node); + return S_OK; +} + static const IWineUiaEventVtbl uia_event_vtbl = { uia_event_QueryInterface, uia_event_AddRef, uia_event_Release, uia_event_advise_events, uia_event_set_event_data, + uia_event_raise_event, };
static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) @@ -1009,17 +1153,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, struct uia_event *event) { - SAFEARRAY *out_req; - BSTR tree_struct; - HRESULT hr; + HRESULT hr = S_OK;
- hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req, - &tree_struct); - if (SUCCEEDED(hr)) + if (event->event_type == EVENT_TYPE_CLIENTSIDE) { - event->u.clientside.cback(&args->simple_args, out_req, tree_struct); - SafeArrayDestroy(out_req); - SysFreeString(tree_struct); + SAFEARRAY *out_req; + BSTR tree_struct; + + hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req, + &tree_struct); + if (SUCCEEDED(hr)) + { + event->u.clientside.cback(&args->simple_args, out_req, tree_struct); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + } + else + { + VARIANT v; + + get_variant_for_node(node, &v); + IWineUiaNode_AddRef((IWineUiaNode *)node); + hr = uia_event_queue_post_event(event, v, args); + if (FAILED(hr)) + UiaNodeRelease(node); }
return hr; @@ -1056,6 +1214,12 @@ static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct uia if (!(event->scope & (TreeScope_Descendants | TreeScope_Children))) return S_OK;
+ if (event->event_type == EVENT_TYPE_SERVERSIDE) + { + FIXME("Matching serverside events through navigation currently unimplemented\n"); + return S_OK; + } + V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY; V_ARRAY(&prop_cond.Value) = event->runtime_id;
@@ -1140,6 +1304,18 @@ static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct uia_eve break; }
+ 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 = uia_event_check_match(node, sa, args, event); + if (FAILED(hr)) + break; + } + } + uia_event_map_entry_release(event_entry); SafeArrayDestroy(sa); UiaNodeRelease(node); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 5aeab22d3ee..325206c6d90 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -148,6 +148,17 @@ static inline void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static inline void get_variant_for_node(HUIANODE node, VARIANT *v) +{ +#ifdef _WIN64 + V_VT(v) = VT_I8; + V_I8(v) = (UINT64)node; +#else + V_VT(v) = VT_I4; + V_I4(v) = (UINT32)node; +#endif +} + static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) { SIZE_T max_capacity, new_capacity; @@ -184,6 +195,7 @@ HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPE 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; +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;