uiautomationcore: Call IRawElementProviderAdviseEvents methods when events are added or removed.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2023-05-15 14:12:47 -04:00 committed by Alexandre Julliard
parent 973d00db1b
commit a5426f6ace
5 changed files with 184 additions and 28 deletions

View file

@ -13714,12 +13714,12 @@ static const struct prov_method_sequence event_seq1[] = {
static const struct prov_method_sequence event_seq2[] = {
{ &Provider, FRAG_GET_RUNTIME_ID },
{ &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO },
{ &Provider, FRAG_GET_FRAGMENT_ROOT },
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */
NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */
{ &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */
{ &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO },
{ &Provider, ADVISE_EVENTS_EVENT_ADDED },
{ 0 },
};
@ -13737,25 +13737,25 @@ static const struct prov_method_sequence event_seq3[] = {
};
static const struct prov_method_sequence event_seq4[] = {
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED },
{ 0 },
};
static const struct prov_method_sequence event_seq5[] = {
{ &Provider, FRAG_GET_RUNTIME_ID },
{ &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO },
{ &Provider, FRAG_GET_FRAGMENT_ROOT },
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */
{ &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, METHOD_TODO },
NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */
{ &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */
{ &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO },
{ &Provider, ADVISE_EVENTS_EVENT_ADDED },
{ 0 },
};
static const struct prov_method_sequence event_seq6[] = {
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED },
{ &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ 0 },
@ -13812,7 +13812,7 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param)
ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref);
todo_wine ok(Provider2.last_call_tid == data->exp_thread_id ||
broken(Provider2.last_call_tid == GetCurrentThreadId()), "Expected method call on separate thread\n");
todo_wine ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId,
ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId,
"Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id);
CoUninitialize();
@ -13931,7 +13931,7 @@ static void test_UiaAddEvent(void)
&event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok_method_sequence(event_seq2, "event_seq2");
/*
@ -13965,7 +13965,7 @@ static void test_UiaAddEvent(void)
&event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */
@ -14005,7 +14005,7 @@ static void test_UiaAddEvent(void)
&event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok_method_sequence(event_seq5, "event_seq5");
/*
@ -14052,7 +14052,7 @@ static void test_UiaAddEvent(void)
&cache_req, &event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
/* Raised an event on Provider_child_child. */
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
@ -14086,9 +14086,9 @@ static void test_UiaAddEvent(void)
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
todo_wine ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref);
ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref);
ok(!Provider.advise_events_added_event_id, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id);
todo_wine ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
"Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id);
thread_data.exp_thread_id = GetCurrentThreadId();
@ -14120,10 +14120,10 @@ static void test_UiaAddEvent(void)
&cache_req, &event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
todo_wine ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
"Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id);
todo_wine ok(Provider_child.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
"Unexpected advise event added, event ID %d\n", Provider_child.advise_events_added_event_id);

View file

@ -61,6 +61,7 @@ library UIA_wine_private
]
interface IWineUiaEvent : IUnknown
{
HRESULT advise_events([in]BOOL advise_added);
}
[
@ -76,6 +77,7 @@ library UIA_wine_private
HRESULT has_parent([out, retval]BOOL *out_val);
HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val);
HRESULT get_focus([out, retval]VARIANT *ret_val);
HRESULT attach_event([in]LONG_PTR huiaevent);
}
[

View file

@ -462,6 +462,21 @@ static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT
return hr;
}
static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEVENT huiaevent)
{
IWineUiaProvider *prov;
HRESULT hr;
hr = IWineUiaNode_get_provider(node, idx, &prov);
if (FAILED(hr))
return hr;
hr = IWineUiaProvider_attach_event(prov, (LONG_PTR)huiaevent);
IWineUiaProvider_Release(prov);
return hr;
}
/*
* IWineUiaNode interface.
*/
@ -1285,6 +1300,22 @@ exit:
return hr;
}
HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event)
{
struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node);
HRESULT hr = S_OK;
int i;
for (i = 0; i < node_data->prov_count; i++)
{
hr = attach_event_to_node_provider(&node_data->IWineUiaNode_iface, i, (HUIAEVENT)event);
if (FAILED(hr))
return hr;
}
return hr;
}
/*
* IWineUiaProvider interface.
*/
@ -1813,6 +1844,44 @@ static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *o
return hr;
}
static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent)
{
struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
struct uia_event *event = (struct uia_event *)huiaevent;
IRawElementProviderFragmentRoot *elroot;
IRawElementProviderFragment *elfrag;
HRESULT hr;
TRACE("%p, %#Ix\n", iface, huiaevent);
hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag);
if (FAILED(hr))
return S_OK;
hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot);
IRawElementProviderFragment_Release(elfrag);
if (FAILED(hr))
return hr;
if (elroot)
{
IRawElementProviderAdviseEvents *advise_events;
hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents,
(void **)&advise_events);
IRawElementProviderFragmentRoot_Release(elroot);
if (SUCCEEDED(hr))
{
hr = uia_event_add_provider_event_adviser(advise_events, event);
IRawElementProviderAdviseEvents_Release(advise_events);
if (FAILED(hr))
return hr;
}
}
return S_OK;
}
static const IWineUiaProviderVtbl uia_provider_vtbl = {
uia_provider_QueryInterface,
uia_provider_AddRef,
@ -1822,6 +1891,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = {
uia_provider_has_parent,
uia_provider_navigate,
uia_provider_get_focus,
uia_provider_attach_event,
};
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov,
@ -2246,6 +2316,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface
return S_OK;
}
static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent)
{
FIXME("%p, %#Ix: stub\n", iface, huiaevent);
return E_NOTIMPL;
}
static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
uia_nested_node_provider_QueryInterface,
uia_nested_node_provider_AddRef,
@ -2255,6 +2331,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = {
uia_nested_node_provider_has_parent,
uia_nested_node_provider_navigate,
uia_nested_node_provider_get_focus,
uia_nested_node_provider_attach_event,
};
static BOOL is_nested_node_provider(IWineUiaProvider *iface)

View file

@ -22,18 +22,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
struct uia_event
{
IWineUiaEvent IWineUiaEvent_iface;
LONG ref;
SAFEARRAY *runtime_id;
int event_id;
int scope;
UiaEventCallback *cback;
};
/*
* IWineUiaEvent interface.
*/
@ -71,17 +59,55 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface)
TRACE("%p, refcount %ld\n", event, ref);
if (!ref)
{
int i;
SafeArrayDestroy(event->runtime_id);
for (i = 0; i < event->event_advisers_count; i++)
IRawElementProviderAdviseEvents_Release(event->event_advisers[i]);
heap_free(event->event_advisers);
heap_free(event);
}
return ref;
}
static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_added)
{
struct uia_event *event = impl_from_IWineUiaEvent(iface);
HRESULT hr;
int i;
TRACE("%p, %d\n", event, advise_added);
for (i = 0; i < event->event_advisers_count; i++)
{
IRawElementProviderAdviseEvents *adviser = event->event_advisers[i];
if (advise_added)
hr = IRawElementProviderAdviseEvents_AdviseEventAdded(adviser, event->event_id, NULL);
else
hr = IRawElementProviderAdviseEvents_AdviseEventRemoved(adviser, event->event_id, NULL);
if (FAILED(hr))
return hr;
}
/* Once we've advised of removal, no need to keep the advisers around. */
if (!advise_added)
{
for (i = 0; i < event->event_advisers_count; i++)
IRawElementProviderAdviseEvents_Release(event->event_advisers[i]);
heap_free(event->event_advisers);
event->event_advisers_count = event->event_advisers_arr_size = 0;
}
return S_OK;
}
static const IWineUiaEventVtbl uia_event_vtbl = {
uia_event_QueryInterface,
uia_event_AddRef,
uia_event_Release,
uia_event_advise_events,
};
static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface)
@ -112,6 +138,19 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int
return S_OK;
}
HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, 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] = advise_events;
IRawElementProviderAdviseEvents_AddRef(advise_events);
event->event_advisers_count++;
return S_OK;
}
/***********************************************************************
* UiaAddEvent (uiautomationcore.@)
*/
@ -150,9 +189,21 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback
return hr;
}
hr = attach_event_to_uia_node(huianode, event);
if (FAILED(hr))
goto exit;
hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, TRUE);
if (FAILED(hr))
goto exit;
*huiaevent = (HUIAEVENT)event;
return S_OK;
exit:
if (FAILED(hr))
IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
return hr;
}
/***********************************************************************
@ -161,12 +212,17 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback
HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
{
struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent);
HRESULT hr;
TRACE("(%p)\n", event);
if (!event)
return E_INVALIDARG;
hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE);
IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
if (FAILED(hr))
WARN("advise_events failed with hr %#lx\n", hr);
return S_OK;
}

View file

@ -93,6 +93,22 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *
return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface);
}
struct uia_event
{
IWineUiaEvent IWineUiaEvent_iface;
LONG ref;
SAFEARRAY *runtime_id;
int event_id;
int scope;
IRawElementProviderAdviseEvents **event_advisers;
int event_advisers_count;
SIZE_T event_advisers_arr_size;
UiaEventCallback *cback;
};
static inline void variant_init_bool(VARIANT *v, BOOL val)
{
V_VT(v) = VT_BOOL;
@ -139,12 +155,17 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c
HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN;
int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN;
int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN;
HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN;
HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node,
BOOL get_hwnd_providers) DECLSPEC_HIDDEN;
/* uia_com_client.c */
HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */
HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events,
struct uia_event *event) DECLSPEC_HIDDEN;
/* uia_ids.c */
const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;
const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) DECLSPEC_HIDDEN;