From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 14 +- dlls/uiautomationcore/uia_com_client.c | 175 ++++++++++++++++++++- 2 files changed, 178 insertions(+), 11 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 545fc7d0bfc..0dd75ce862f 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13105,11 +13105,11 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati }
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, 1, elem, NULL); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_AutomationFocusChangedEventId, elem, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* * UIA_AutomationFocusChangedEventId can only be listened for with @@ -13131,8 +13131,8 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, 1, elem, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
/* * Test event raising behavior. @@ -13183,8 +13183,8 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
VariantInit(&v); initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); @@ -13213,7 +13213,7 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati /* No removal will occur due to a lack of a runtime ID to match. */ hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
hr = IUIAutomation_RemoveAllEventHandlers(uia_iface); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 55abda5e86d..d5bc353ffd1 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -903,6 +903,7 @@ static HRESULT get_uia_cache_request_struct_from_iface(IUIAutomationCacheRequest */ static struct uia_com_event_handlers { + struct rb_tree handler_map; struct list handler_list;
LONG handler_count; @@ -917,14 +918,48 @@ static CRITICAL_SECTION_DEBUG com_event_handlers_cs_debug = }; static CRITICAL_SECTION com_event_handlers_cs = { &com_event_handlers_cs_debug, -1, 0, 0, 0, 0 };
+struct uia_event_handler_identifier { + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + int event_id; +}; + +struct uia_event_handler_map_entry +{ + struct rb_entry entry; + + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + int event_id; + + struct list handlers_list; +}; + +static int uia_com_event_handler_id_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_event_handler_map_entry *map_entry = RB_ENTRY_VALUE(entry, struct uia_event_handler_map_entry, entry); + struct uia_event_handler_identifier *event_id = (struct uia_event_handler_identifier *)key; + + if (event_id->event_id != map_entry->event_id) + return (event_id->event_id > map_entry->event_id) - (event_id->event_id < map_entry->event_id); + else if (event_id->handler_iface != map_entry->handler_iface) + return (event_id->handler_iface > map_entry->handler_iface) - (event_id->handler_iface < map_entry->handler_iface); + else + return uia_compare_safearrays(event_id->runtime_id, map_entry->runtime_id, UIAutomationType_IntArray); +} + struct uia_com_event { IUnknown *handler_iface; + SAFEARRAY *runtime_id; int event_id;
struct list event_handler_list_entry; + + struct list event_handler_map_list_entry; + struct uia_event_handler_map_entry *handler_map; };
-static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, int event_id) +static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, SAFEARRAY *runtime_id, int event_id) { struct uia_com_event *event; HRESULT hr = S_OK; @@ -932,6 +967,13 @@ static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, int event if (!(event = heap_alloc_zero(sizeof(*event)))) return E_OUTOFMEMORY;
+ if (runtime_id) + { + hr = SafeArrayCopy(runtime_id, &event->runtime_id); + if (FAILED(hr)) + return hr; + } + event->handler_iface = handler_iface; IUnknown_AddRef(handler_iface); event->event_id = event_id; @@ -939,23 +981,113 @@ static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, int event EnterCriticalSection(&com_event_handlers_cs);
if (!com_event_handlers.handler_count) + { list_init(&com_event_handlers.handler_list); + rb_init(&com_event_handlers.handler_map, uia_com_event_handler_id_compare); + } + + if (runtime_id) + { + struct uia_event_handler_identifier event_ident = { handler_iface, runtime_id, event_id }; + struct uia_event_handler_map_entry *event_map; + struct rb_entry *rb_entry; + + if ((rb_entry = rb_get(&com_event_handlers.handler_map, &event_ident))) + event_map = RB_ENTRY_VALUE(rb_entry, struct uia_event_handler_map_entry, entry); + else + { + if (!(event_map = heap_alloc_zero(sizeof(*event_map)))) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + hr = SafeArrayCopy(event->runtime_id, &event_map->runtime_id); + if (FAILED(hr)) + { + heap_free(event_map); + goto exit; + } + + event_map->event_id = event->event_id; + event_map->handler_iface = event->handler_iface; + IUnknown_AddRef(event_map->handler_iface); + + list_init(&event_map->handlers_list); + rb_put(&com_event_handlers.handler_map, &event_ident, &event_map->entry); + } + + list_add_tail(&event_map->handlers_list, &event->event_handler_map_list_entry); + event->handler_map = event_map; + }
list_add_tail(&com_event_handlers.handler_list, &event->event_handler_list_entry); com_event_handlers.handler_count++;
+exit: LeaveCriticalSection(&com_event_handlers_cs);
+ if (FAILED(hr)) + { + IUnknown_Release(event->handler_iface); + SafeArrayDestroy(event->runtime_id); + heap_free(event); + } + return hr; }
+static void uia_event_handler_map_entry_destroy(struct uia_event_handler_map_entry *event_map) +{ + rb_remove(&com_event_handlers.handler_map, &event_map->entry); + IUnknown_Release(event_map->handler_iface); + SafeArrayDestroy(event_map->runtime_id); + heap_free(event_map); +} + static void uia_event_handlers_destroy_handler(struct uia_com_event *event) { list_remove(&event->event_handler_list_entry); IUnknown_Release(event->handler_iface); + SafeArrayDestroy(event->runtime_id); + if (event->handler_map) + { + list_remove(&event->event_handler_map_list_entry); + if (list_empty(&event->handler_map->handlers_list)) + uia_event_handler_map_entry_destroy(event->handler_map); + } heap_free(event); }
+static void uia_event_handlers_remove_handlers(IUnknown *handler_iface, SAFEARRAY *runtime_id, int event_id) +{ + struct uia_event_handler_identifier event_ident = { handler_iface, runtime_id, event_id }; + struct rb_entry *rb_entry; + + EnterCriticalSection(&com_event_handlers_cs); + + if (com_event_handlers.handler_count && (rb_entry = rb_get(&com_event_handlers.handler_map, &event_ident))) + { + struct uia_event_handler_map_entry *event_map = RB_ENTRY_VALUE(rb_entry, struct uia_event_handler_map_entry, entry); + struct list *cursor, *cursor2; + + LIST_FOR_EACH_SAFE(cursor, cursor2, &event_map->handlers_list) + { + struct uia_com_event *event = LIST_ENTRY(cursor, struct uia_com_event, event_handler_map_list_entry); + + list_remove(cursor); + /* Set to NULL to prevent destruction in uia_event_handlers_destroy_handler. */ + event->handler_map = NULL; + uia_event_handlers_destroy_handler(event); + com_event_handlers.handler_count--; + } + + uia_event_handler_map_entry_destroy(event_map); + } + + LeaveCriticalSection(&com_event_handlers_cs); +} + /* * IUIAutomationElementArray interface. */ @@ -3110,7 +3242,9 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, IUIAutomationEventHandler *handler) { + struct uia_element *element; IUnknown *handler_iface; + SAFEARRAY *runtime_id; HRESULT hr;
TRACE("%p, %d, %p, %#x, %p, %p\n", iface, event_id, elem, scope, cache_req, handler); @@ -3125,8 +3259,16 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, if (FAILED(hr)) return hr;
- hr = uia_event_handlers_add_handler(handler_iface, event_id); + element = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)elem); + hr = UiaGetRuntimeId(element->node, &runtime_id); + if (FAILED(hr)) + goto exit; + + hr = uia_event_handlers_add_handler(handler_iface, runtime_id, event_id); + +exit: IUnknown_Release(handler_iface); + SafeArrayDestroy(runtime_id);
return hr; } @@ -3134,8 +3276,33 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, IUIAutomationElement *elem, IUIAutomationEventHandler *handler) { - FIXME("%p, %d, %p, %p: stub\n", iface, event_id, elem, handler); - return E_NOTIMPL; + struct uia_element *element; + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + HRESULT hr; + + TRACE("%p, %d, %p, %p\n", iface, event_id, elem, handler); + + if (!elem || !handler) + return S_OK; + + element = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)elem); + hr = UiaGetRuntimeId(element->node, &runtime_id); + if (FAILED(hr) || !runtime_id) + return hr; + + hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_iface); + if (FAILED(hr)) + { + SafeArrayDestroy(runtime_id); + return hr; + } + + uia_event_handlers_remove_handlers(handler_iface, runtime_id, event_id); + IUnknown_Release(handler_iface); + SafeArrayDestroy(runtime_id); + + return S_OK; }
static HRESULT WINAPI uia_iface_AddPropertyChangedEventHandlerNativeArray(IUIAutomation6 *iface,