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)
SET_EXPECT_MULTI(prov_callback_proxy, 3);
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Descendants,
NULL, 0, &cache_req, &event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!event, "event == NULL\n");
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n");
CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2);
CHECK_CALLED_AT_MOST(prov_callback_nonclient, 3);
......@@ -14070,7 +14070,7 @@ static void test_UiaAddEvent_client_proc(void)
todo_wine CHECK_CALLED(uia_event_callback);
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);
/*
......
......@@ -68,6 +68,7 @@ library UIA_wine_private
object,
uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba),
pointer_default(unique),
oleautomation,
]
interface IWineUiaEvent : IUnknown
{
......@@ -102,5 +103,6 @@ library UIA_wine_private
HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val);
HRESULT disconnect();
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)
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 = {
uia_node_QueryInterface,
uia_node_AddRef,
......@@ -547,6 +578,7 @@ static const IWineUiaNodeVtbl uia_node_vtbl = {
uia_node_get_prop_val,
uia_node_disconnect,
uia_node_get_hwnd,
uia_node_attach_event,
};
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
static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent)
{
FIXME("%p, %#Ix: stub\n", iface, huiaevent);
return E_NOTIMPL;
struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface);
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 = {
......
......@@ -225,7 +225,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface)
assert(!event->event_map_entry);
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++)
IWineUiaEventAdviser_Release(event->event_advisers[i]);
heap_free(event->event_advisers);
......@@ -298,12 +306,42 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int
event->runtime_id = runtime_id;
event->event_id = event_id;
event->scope = scope;
event->cback = cback;
event->u.clientside.cback = cback;
event->event_type = EVENT_TYPE_CLIENTSIDE;
*out_event = event;
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.
*/
......@@ -438,19 +476,113 @@ HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *ad
adv_events->advise_events = advise_events;
IRawElementProviderAdviseEvents_AddRef(advise_events);
if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size,
event->event_advisers_count + 1, sizeof(*event->event_advisers)))
hr = uia_event_add_event_adviser(&adv_events->IWineUiaEventAdviser_iface, event);
IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface);
return hr;
}
/*
* 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)
{
IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface);
return E_OUTOFMEMORY;
IWineUiaEvent_Release(adv_events->event_iface);
heap_free(adv_events);
}
return ref;
}
event->event_advisers[event->event_advisers_count] = &adv_events->IWineUiaEventAdviser_iface;
event->event_advisers_count++;
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;
}
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.@)
*/
......@@ -489,7 +621,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback
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))
goto exit;
......@@ -520,6 +652,7 @@ exit:
HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
{
struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent);
IWineUiaEvent *event_iface;
HRESULT hr;
TRACE("(%p)\n", event);
......@@ -527,8 +660,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
if (!event)
return E_INVALIDARG;
hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE);
IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
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);
}
else
event_iface = &event->IWineUiaEvent_iface;
hr = IWineUiaEvent_advise_events(event_iface, FALSE);
IWineUiaEvent_Release(event_iface);
if (FAILED(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
BSTR tree_struct;
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);
if (SUCCEEDED(hr))
{
event->cback(args, out_req, tree_struct);
event->u.clientside.cback(args, out_req, tree_struct);
SafeArrayDestroy(out_req);
SysFreeString(tree_struct);
}
......
......@@ -93,6 +93,11 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *
return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface);
}
enum uia_event_type {
EVENT_TYPE_CLIENTSIDE,
EVENT_TYPE_SERVERSIDE,
};
struct uia_event
{
IWineUiaEvent IWineUiaEvent_iface;
......@@ -111,8 +116,29 @@ struct uia_event
struct uia_event_map_entry *event_map_entry;
LONG event_defunct;
struct UiaCacheRequest cache_req;
UiaEventCallback *cback;
int event_type;
union
{
struct {
struct UiaCacheRequest cache_req;
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)
......@@ -170,8 +196,10 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN;
HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* 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,
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 */
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