Commit 4cc7525a authored by Connor McAdams's avatar Connor McAdams Committed by Alexandre Julliard

uiautomationcore: Partially implement IUIAutomation::AddAutomationEventHandler.

parent 79313d01
...@@ -13089,11 +13089,11 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati ...@@ -13089,11 +13089,11 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
*/ */
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, NULL, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, NULL, TreeScope_SubTree, NULL,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL,
NULL); NULL);
todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
/* /*
* Passing in a NULL element to this method results in an access violation * Passing in a NULL element to this method results in an access violation
...@@ -13118,21 +13118,21 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati ...@@ -13118,21 +13118,21 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
*/ */
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_AutomationFocusChangedEventId, elem, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_AutomationFocusChangedEventId, elem, TreeScope_SubTree, NULL,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* Windows 11 queries the HWND for the element when adding a new handler. */ /* Windows 11 queries the HWND for the element when adding a new handler. */
set_uia_hwnd_expects(3, 2, 2, 3, 0); set_uia_hwnd_expects(3, 2, 2, 3, 0);
/* All other event IDs are fine, only focus events are blocked. */ /* All other event IDs are fine, only focus events are blocked. */
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, 1, elem, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, 1, elem, TreeScope_SubTree, NULL,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0); check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0);
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, 1, elem, hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, 1, elem,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
/* /*
* Test event raising behavior. * Test event raising behavior.
...@@ -13140,8 +13140,8 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati ...@@ -13140,8 +13140,8 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
set_uia_hwnd_expects(3, 2, 2, 3, 0); set_uia_hwnd_expects(3, 2, 2, 3, 0);
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0); check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0);
/* Same behavior as HUIAEVENTs, events are matched by runtime ID. */ /* Same behavior as HUIAEVENTs, events are matched by runtime ID. */
...@@ -13184,7 +13184,7 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati ...@@ -13184,7 +13184,7 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
VariantInit(&v); VariantInit(&v);
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
...@@ -13207,18 +13207,18 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati ...@@ -13207,18 +13207,18 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati
*/ */
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, TreeScope_SubTree, NULL, hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, TreeScope_SubTree, NULL,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
/* No removal will occur due to a lack of a runtime ID to match. */ /* No removal will occur due to a lack of a runtime ID to match. */
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2,
&AutomationEventHandler.IUIAutomationEventHandler_iface); &AutomationEventHandler.IUIAutomationEventHandler_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
hr = IUIAutomation_RemoveAllEventHandlers(uia_iface); hr = IUIAutomation_RemoveAllEventHandlers(uia_iface);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
IUIAutomationElement_Release(elem2); IUIAutomationElement_Release(elem2);
} }
......
...@@ -899,6 +899,123 @@ static HRESULT get_uia_cache_request_struct_from_iface(IUIAutomationCacheRequest ...@@ -899,6 +899,123 @@ static HRESULT get_uia_cache_request_struct_from_iface(IUIAutomationCacheRequest
} }
/* /*
* COM API UI Automation event related functions.
*/
static struct uia_com_event_handlers
{
struct rb_tree handler_map;
LONG handler_count;
} com_event_handlers;
static CRITICAL_SECTION com_event_handlers_cs;
static CRITICAL_SECTION_DEBUG com_event_handlers_cs_debug =
{
0, 0, &com_event_handlers_cs,
{ &com_event_handlers_cs_debug.ProcessLocksList, &com_event_handlers_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": com_event_handlers_cs") }
};
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 if (event_id->runtime_id && map_entry->runtime_id)
return uia_compare_safearrays(event_id->runtime_id, map_entry->runtime_id, UIAutomationType_IntArray);
else
return (event_id->runtime_id > map_entry->runtime_id) - (event_id->runtime_id < map_entry->runtime_id);
}
struct uia_com_event {
IUnknown *handler_iface;
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, SAFEARRAY *runtime_id, int event_id)
{
struct uia_event_handler_identifier event_ident = { handler_iface, runtime_id, event_id };
struct uia_event_handler_map_entry *event_map;
struct uia_com_event *event;
struct rb_entry *rb_entry;
HRESULT hr = S_OK;
if (!(event = heap_alloc_zero(sizeof(*event))))
return E_OUTOFMEMORY;
event->handler_iface = handler_iface;
IUnknown_AddRef(handler_iface);
EnterCriticalSection(&com_event_handlers_cs);
if (!com_event_handlers.handler_count)
rb_init(&com_event_handlers.handler_map, uia_com_event_handler_id_compare);
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(runtime_id, &event_map->runtime_id);
if (FAILED(hr))
{
heap_free(event_map);
goto exit;
}
event_map->event_id = 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;
com_event_handlers.handler_count++;
exit:
LeaveCriticalSection(&com_event_handlers_cs);
if (FAILED(hr))
{
IUnknown_Release(event->handler_iface);
heap_free(event);
}
return hr;
}
/*
* IUIAutomationElementArray interface. * IUIAutomationElementArray interface.
*/ */
struct uia_element_array { struct uia_element_array {
...@@ -3052,8 +3169,35 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, ...@@ -3052,8 +3169,35 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface,
IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req,
IUIAutomationEventHandler *handler) IUIAutomationEventHandler *handler)
{ {
FIXME("%p, %d, %p, %#x, %p, %p: stub\n", iface, event_id, elem, scope, cache_req, handler); struct uia_element *element;
return E_NOTIMPL; IUnknown *handler_iface;
SAFEARRAY *runtime_id;
HRESULT hr;
TRACE("%p, %d, %p, %#x, %p, %p\n", iface, event_id, elem, scope, cache_req, handler);
if (!elem || !handler)
return E_POINTER;
if (event_id == UIA_AutomationFocusChangedEventId)
return E_INVALIDARG;
hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_iface);
if (FAILED(hr))
return hr;
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;
} }
static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment