Commit 549a276c authored by Connor McAdams's avatar Connor McAdams Committed by Alexandre Julliard

uiautomationcore: Add support for creating serverside events.

parent fa02af30
...@@ -14027,8 +14027,8 @@ static void test_UiaAddEvent_client_proc(void) ...@@ -14027,8 +14027,8 @@ static void test_UiaAddEvent_client_proc(void)
SET_EXPECT_MULTI(prov_callback_proxy, 3); SET_EXPECT_MULTI(prov_callback_proxy, 3);
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Descendants, hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Descendants,
NULL, 0, &cache_req, &event); NULL, 0, &cache_req, &event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!event, "event == NULL\n"); ok(!!event, "event == NULL\n");
ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n");
CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2); CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2);
CHECK_CALLED_AT_MOST(prov_callback_nonclient, 3); CHECK_CALLED_AT_MOST(prov_callback_nonclient, 3);
...@@ -14070,7 +14070,7 @@ static void test_UiaAddEvent_client_proc(void) ...@@ -14070,7 +14070,7 @@ static void test_UiaAddEvent_client_proc(void)
todo_wine CHECK_CALLED(uia_event_callback); todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE);
/* /*
......
...@@ -68,6 +68,7 @@ library UIA_wine_private ...@@ -68,6 +68,7 @@ library UIA_wine_private
object, object,
uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba), uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba),
pointer_default(unique), pointer_default(unique),
oleautomation,
] ]
interface IWineUiaEvent : IUnknown interface IWineUiaEvent : IUnknown
{ {
...@@ -102,5 +103,6 @@ library UIA_wine_private ...@@ -102,5 +103,6 @@ library UIA_wine_private
HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val); HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val);
HRESULT disconnect(); HRESULT disconnect();
HRESULT get_hwnd([out, retval]ULONG *out_hwnd); HRESULT get_hwnd([out, retval]ULONG *out_hwnd);
HRESULT attach_event([out, retval]IWineUiaEvent **ret_event);
} }
} }
...@@ -539,6 +539,37 @@ static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd) ...@@ -539,6 +539,37 @@ static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd)
return S_OK; return S_OK;
} }
static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, IWineUiaEvent **ret_event)
{
struct uia_node *node = impl_from_IWineUiaNode(iface);
struct uia_event *event = NULL;
HRESULT hr;
TRACE("%p, %p\n", node, ret_event);
*ret_event = NULL;
hr = create_serverside_uia_event(&event);
if (FAILED(hr))
return hr;
hr = attach_event_to_node_provider(iface, 0, (HUIAEVENT)event);
if (FAILED(hr))
{
IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
return hr;
}
/*
* Attach this nested node to the serverside event to keep the provider
* thread alive.
*/
IWineUiaNode_AddRef(iface);
event->u.serverside.node = iface;
*ret_event = &event->IWineUiaEvent_iface;
return hr;
}
static const IWineUiaNodeVtbl uia_node_vtbl = { static const IWineUiaNodeVtbl uia_node_vtbl = {
uia_node_QueryInterface, uia_node_QueryInterface,
uia_node_AddRef, uia_node_AddRef,
...@@ -547,6 +578,7 @@ static const IWineUiaNodeVtbl uia_node_vtbl = { ...@@ -547,6 +578,7 @@ static const IWineUiaNodeVtbl uia_node_vtbl = {
uia_node_get_prop_val, uia_node_get_prop_val,
uia_node_disconnect, uia_node_disconnect,
uia_node_get_hwnd, uia_node_get_hwnd,
uia_node_attach_event,
}; };
static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface) static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface)
...@@ -2243,8 +2275,21 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface ...@@ -2243,8 +2275,21 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface
static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent)
{ {
FIXME("%p, %#Ix: stub\n", iface, huiaevent); struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
return E_NOTIMPL; struct uia_event *event = (struct uia_event *)huiaevent;
IWineUiaEvent *remote_event = NULL;
HRESULT hr;
TRACE("%p, %#Ix\n", iface, huiaevent);
hr = IWineUiaNode_attach_event(prov->nested_node, &remote_event);
if (FAILED(hr) || !remote_event)
return hr;
hr = uia_event_add_serverside_event_adviser(remote_event, event);
IWineUiaEvent_Release(remote_event);
return hr;
} }
static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
......
...@@ -225,7 +225,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) ...@@ -225,7 +225,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface)
assert(!event->event_map_entry); assert(!event->event_map_entry);
SafeArrayDestroy(event->runtime_id); SafeArrayDestroy(event->runtime_id);
uia_cache_request_destroy(&event->cache_req); if (event->event_type == EVENT_TYPE_CLIENTSIDE)
{
uia_cache_request_destroy(&event->u.clientside.cache_req);
if (event->u.clientside.mta_cookie)
CoDecrementMTAUsage(event->u.clientside.mta_cookie);
}
else if (event->u.serverside.node)
IWineUiaNode_Release(event->u.serverside.node);
for (i = 0; i < event->event_advisers_count; i++) for (i = 0; i < event->event_advisers_count; i++)
IWineUiaEventAdviser_Release(event->event_advisers[i]); IWineUiaEventAdviser_Release(event->event_advisers[i]);
heap_free(event->event_advisers); heap_free(event->event_advisers);
...@@ -298,12 +306,42 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int ...@@ -298,12 +306,42 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int
event->runtime_id = runtime_id; event->runtime_id = runtime_id;
event->event_id = event_id; event->event_id = event_id;
event->scope = scope; event->scope = scope;
event->cback = cback; event->u.clientside.cback = cback;
event->event_type = EVENT_TYPE_CLIENTSIDE;
*out_event = event; *out_event = event;
return S_OK; return S_OK;
} }
HRESULT create_serverside_uia_event(struct uia_event **out_event)
{
struct uia_event *event = heap_alloc_zero(sizeof(*event));
*out_event = NULL;
if (!event)
return E_OUTOFMEMORY;
event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl;
event->ref = 1;
event->event_type = EVENT_TYPE_SERVERSIDE;
*out_event = event;
return S_OK;
}
static HRESULT uia_event_add_event_adviser(IWineUiaEventAdviser *adviser, struct uia_event *event)
{
if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size,
event->event_advisers_count + 1, sizeof(*event->event_advisers)))
return E_OUTOFMEMORY;
event->event_advisers[event->event_advisers_count] = adviser;
IWineUiaEventAdviser_AddRef(adviser);
event->event_advisers_count++;
return S_OK;
}
/* /*
* IWineUiaEventAdviser interface. * IWineUiaEventAdviser interface.
*/ */
...@@ -438,19 +476,113 @@ HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *ad ...@@ -438,19 +476,113 @@ HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *ad
adv_events->advise_events = advise_events; adv_events->advise_events = advise_events;
IRawElementProviderAdviseEvents_AddRef(advise_events); IRawElementProviderAdviseEvents_AddRef(advise_events);
if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, hr = uia_event_add_event_adviser(&adv_events->IWineUiaEventAdviser_iface, event);
event->event_advisers_count + 1, sizeof(*event->event_advisers)))
{
IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface); IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface);
return E_OUTOFMEMORY;
}
event->event_advisers[event->event_advisers_count] = &adv_events->IWineUiaEventAdviser_iface; return hr;
event->event_advisers_count++; }
/*
* IWineUiaEventAdviser interface for serverside events.
*/
struct uia_serverside_event_adviser {
IWineUiaEventAdviser IWineUiaEventAdviser_iface;
LONG ref;
IWineUiaEvent *event_iface;
};
static inline struct uia_serverside_event_adviser *impl_from_serverside_IWineUiaEventAdviser(IWineUiaEventAdviser *iface)
{
return CONTAINING_RECORD(iface, struct uia_serverside_event_adviser, IWineUiaEventAdviser_iface);
}
static HRESULT WINAPI uia_serverside_event_adviser_QueryInterface(IWineUiaEventAdviser *iface, REFIID riid, void **ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, &IID_IWineUiaEventAdviser) || IsEqualIID(riid, &IID_IUnknown))
*ppv = iface;
else
return E_NOINTERFACE;
IWineUiaEventAdviser_AddRef(iface);
return S_OK;
}
static ULONG WINAPI uia_serverside_event_adviser_AddRef(IWineUiaEventAdviser *iface)
{
struct uia_serverside_event_adviser *adv_events = impl_from_serverside_IWineUiaEventAdviser(iface);
ULONG ref = InterlockedIncrement(&adv_events->ref);
TRACE("%p, refcount %ld\n", adv_events, ref);
return ref;
}
static ULONG WINAPI uia_serverside_event_adviser_Release(IWineUiaEventAdviser *iface)
{
struct uia_serverside_event_adviser *adv_events = impl_from_serverside_IWineUiaEventAdviser(iface);
ULONG ref = InterlockedDecrement(&adv_events->ref);
TRACE("%p, refcount %ld\n", adv_events, ref);
if (!ref)
{
IWineUiaEvent_Release(adv_events->event_iface);
heap_free(adv_events);
}
return ref;
}
static HRESULT WINAPI uia_serverside_event_adviser_advise(IWineUiaEventAdviser *iface, BOOL advise_added, LONG_PTR huiaevent)
{
FIXME("%p, %d, %#Ix: stub\n", iface, advise_added, huiaevent);
return S_OK; return S_OK;
} }
static const IWineUiaEventAdviserVtbl uia_serverside_event_adviser_vtbl = {
uia_serverside_event_adviser_QueryInterface,
uia_serverside_event_adviser_AddRef,
uia_serverside_event_adviser_Release,
uia_serverside_event_adviser_advise,
};
HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event)
{
struct uia_serverside_event_adviser *adv_events;
HRESULT hr;
/*
* Need to create a proxy IWineUiaEvent for our clientside event to use
* this serverside IWineUiaEvent proxy from the appropriate apartment.
*/
if (!event->u.clientside.git_cookie)
{
hr = CoIncrementMTAUsage(&event->u.clientside.mta_cookie);
if (FAILED(hr))
return hr;
hr = register_interface_in_git((IUnknown *)&event->IWineUiaEvent_iface, &IID_IWineUiaEvent,
&event->u.clientside.git_cookie);
if (FAILED(hr))
{
CoDecrementMTAUsage(event->u.clientside.mta_cookie);
return hr;
}
}
if (!(adv_events = heap_alloc_zero(sizeof(*adv_events))))
return E_OUTOFMEMORY;
adv_events->IWineUiaEventAdviser_iface.lpVtbl = &uia_serverside_event_adviser_vtbl;
adv_events->ref = 1;
adv_events->event_iface = serverside_event;
IWineUiaEvent_AddRef(serverside_event);
hr = uia_event_add_event_adviser(&adv_events->IWineUiaEventAdviser_iface, event);
IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface);
return hr;
}
/*********************************************************************** /***********************************************************************
* UiaAddEvent (uiautomationcore.@) * UiaAddEvent (uiautomationcore.@)
*/ */
...@@ -489,7 +621,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback ...@@ -489,7 +621,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback
return hr; return hr;
} }
hr = uia_cache_request_clone(&event->cache_req, cache_req); hr = uia_cache_request_clone(&event->u.clientside.cache_req, cache_req);
if (FAILED(hr)) if (FAILED(hr))
goto exit; goto exit;
...@@ -520,6 +652,7 @@ exit: ...@@ -520,6 +652,7 @@ exit:
HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
{ {
struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent);
IWineUiaEvent *event_iface;
HRESULT hr; HRESULT hr;
TRACE("(%p)\n", event); TRACE("(%p)\n", event);
...@@ -527,8 +660,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) ...@@ -527,8 +660,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
if (!event) if (!event)
return E_INVALIDARG; return E_INVALIDARG;
hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE); assert(event->event_type == EVENT_TYPE_CLIENTSIDE);
if (event->u.clientside.git_cookie)
{
hr = get_interface_in_git(&IID_IWineUiaEvent, event->u.clientside.git_cookie, (IUnknown **)&event_iface);
if (FAILED(hr))
return hr;
hr = unregister_interface_in_git(event->u.clientside.git_cookie);
if (FAILED(hr))
{
IWineUiaEvent_Release(event_iface);
return hr;
}
/*
* We want the release of the event_iface proxy to set the reference
* count to 0, so we release our reference here.
*/
IWineUiaEvent_Release(&event->IWineUiaEvent_iface); IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
}
else
event_iface = &event->IWineUiaEvent_iface;
hr = IWineUiaEvent_advise_events(event_iface, FALSE);
IWineUiaEvent_Release(event_iface);
if (FAILED(hr)) if (FAILED(hr))
WARN("advise_events failed with hr %#lx\n", hr); WARN("advise_events failed with hr %#lx\n", hr);
...@@ -541,11 +697,11 @@ static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct ...@@ -541,11 +697,11 @@ static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct
BSTR tree_struct; BSTR tree_struct;
HRESULT hr; HRESULT hr;
hr = UiaGetUpdatedCache(node, &event->cache_req, NormalizeState_View, NULL, &out_req, hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req,
&tree_struct); &tree_struct);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
event->cback(args, out_req, tree_struct); event->u.clientside.cback(args, out_req, tree_struct);
SafeArrayDestroy(out_req); SafeArrayDestroy(out_req);
SysFreeString(tree_struct); SysFreeString(tree_struct);
} }
......
...@@ -93,6 +93,11 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * ...@@ -93,6 +93,11 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *
return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface);
} }
enum uia_event_type {
EVENT_TYPE_CLIENTSIDE,
EVENT_TYPE_SERVERSIDE,
};
struct uia_event struct uia_event
{ {
IWineUiaEvent IWineUiaEvent_iface; IWineUiaEvent IWineUiaEvent_iface;
...@@ -111,8 +116,29 @@ struct uia_event ...@@ -111,8 +116,29 @@ struct uia_event
struct uia_event_map_entry *event_map_entry; struct uia_event_map_entry *event_map_entry;
LONG event_defunct; LONG event_defunct;
int event_type;
union
{
struct {
struct UiaCacheRequest cache_req; struct UiaCacheRequest cache_req;
UiaEventCallback *cback; UiaEventCallback *cback;
/*
* This is temporarily used to keep the MTA alive prior to our
* introduction of a dedicated event thread.
*/
CO_MTA_USAGE_COOKIE mta_cookie;
DWORD git_cookie;
} clientside;
struct {
/*
* Similar to the client MTA cookie, used to keep the provider
* thread alive as a temporary measure before introducing the
* event thread.
*/
IWineUiaNode *node;
} serverside;
} u;
}; };
static inline void variant_init_bool(VARIANT *v, BOOL val) static inline void variant_init_bool(VARIANT *v, BOOL val)
...@@ -170,8 +196,10 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; ...@@ -170,8 +196,10 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN;
HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN; HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ /* uia_event.c */
HRESULT create_serverside_uia_event(struct uia_event **out_event) DECLSPEC_HIDDEN;
HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events,
struct uia_event *event) DECLSPEC_HIDDEN; struct uia_event *event) DECLSPEC_HIDDEN;
HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) DECLSPEC_HIDDEN;
/* uia_ids.c */ /* uia_ids.c */
const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;
......
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