Commit 407ce9a1 authored by Connor McAdams's avatar Connor McAdams Committed by Alexandre Julliard

uiautomationcore: Implement UiaAddEvent.

parent a3e71d4c
......@@ -7,6 +7,7 @@ EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \
uia_client.c \
uia_com_client.c \
uia_event.c \
uia_ids.c \
uia_main.c \
uia_provider.c
......
......@@ -13819,6 +13819,45 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param)
return 0;
}
static void test_UiaAddEvent_args(HUIANODE node)
{
struct UiaCacheRequest cache_req;
HUIAEVENT event;
HRESULT hr;
set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0,
AutomationElementMode_Full);
/* NULL node. */
hr = UiaAddEvent(NULL, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req,
&event);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* NULL event callback. */
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, NULL, TreeScope_Element, NULL, 0, &cache_req,
&event);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* NULL cache request. */
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, NULL,
&event);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* NULL event handle. */
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req,
NULL);
ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* Event IDs aren't checked for validity, 1 is not a valid UIA event ID. */
event = NULL;
hr = UiaAddEvent(node, 1, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!event, "event == NULL\n");
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
}
static void test_UiaAddEvent(void)
{
IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface,
......@@ -13855,6 +13894,9 @@ static void test_UiaAddEvent(void)
check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE);
VariantClear(&v);
/* Test valid function input arguments. */
test_UiaAddEvent_args(node);
/*
* Raise event without any registered event handlers.
*/
......@@ -13875,11 +13917,10 @@ static void test_UiaAddEvent(void)
AutomationElementMode_Full);
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, 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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq2, "event_seq2");
ok_method_sequence(event_seq2, "event_seq2");
/*
* Even though we raise an event on the same provider as the one our node
......@@ -13911,11 +13952,10 @@ static void test_UiaAddEvent(void)
Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef;
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, 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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq2, "event_seq2");
ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */
method_sequences_enabled = FALSE;
......@@ -13953,11 +13993,10 @@ static void test_UiaAddEvent(void)
/* Create an event with TreeScope_Children. */
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Children, 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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq5, "event_seq5");
ok_method_sequence(event_seq5, "event_seq5");
/*
* Only TreeScope_Children and not TreeScope_Element, handler won't be
......@@ -14001,8 +14040,8 @@ static void test_UiaAddEvent(void)
/* Create an event with TreeScope_Descendants. */
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | 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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
/* Raised an event on Provider_child_child. */
......@@ -14034,8 +14073,8 @@ static void test_UiaAddEvent(void)
Provider.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface;
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | 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(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
todo_wine 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);
......@@ -14069,8 +14108,8 @@ static void test_UiaAddEvent(void)
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | 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");
todo_wine 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);
......@@ -14132,8 +14171,8 @@ static void test_UiaAddEvent(void)
AutomationElementMode_Full);
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Children, 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");
/*
* Raising an event on a serverside provider results in no clientside
......
......@@ -56,6 +56,15 @@ library UIA_wine_private
[
object,
uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba),
pointer_default(unique),
]
interface IWineUiaEvent : IUnknown
{
}
[
object,
uuid(57865755-6c05-4522-98df-4ca658b768ef),
pointer_default(unique),
oleautomation,
......
......@@ -3514,17 +3514,6 @@ exit:
}
/***********************************************************************
* UiaAddEvent (uiautomationcore.@)
*/
HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope,
PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent)
{
FIXME("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count,
cache_req, huiaevent);
return E_NOTIMPL;
}
/***********************************************************************
* UiaRemoveEvent (uiautomationcore.@)
*/
HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
......
/*
* Copyright 2023 Connor McAdams for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "uia_private.h"
#include "wine/debug.h"
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.
*/
static inline struct uia_event *impl_from_IWineUiaEvent(IWineUiaEvent *iface)
{
return CONTAINING_RECORD(iface, struct uia_event, IWineUiaEvent_iface);
}
static HRESULT WINAPI uia_event_QueryInterface(IWineUiaEvent *iface, REFIID riid, void **ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, &IID_IWineUiaEvent) || IsEqualIID(riid, &IID_IUnknown))
*ppv = iface;
else
return E_NOINTERFACE;
IWineUiaEvent_AddRef(iface);
return S_OK;
}
static ULONG WINAPI uia_event_AddRef(IWineUiaEvent *iface)
{
struct uia_event *event = impl_from_IWineUiaEvent(iface);
ULONG ref = InterlockedIncrement(&event->ref);
TRACE("%p, refcount %ld\n", event, ref);
return ref;
}
static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface)
{
struct uia_event *event = impl_from_IWineUiaEvent(iface);
ULONG ref = InterlockedDecrement(&event->ref);
TRACE("%p, refcount %ld\n", event, ref);
if (!ref)
{
SafeArrayDestroy(event->runtime_id);
heap_free(event);
}
return ref;
}
static const IWineUiaEventVtbl uia_event_vtbl = {
uia_event_QueryInterface,
uia_event_AddRef,
uia_event_Release,
};
static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback,
SAFEARRAY *runtime_id)
{
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->runtime_id = runtime_id;
event->event_id = event_id;
event->scope = scope;
event->cback = cback;
*out_event = event;
return S_OK;
}
/***********************************************************************
* UiaAddEvent (uiautomationcore.@)
*/
HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope,
PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent)
{
const struct uia_event_info *event_info = uia_event_info_from_id(event_id);
struct uia_event *event;
SAFEARRAY *sa;
HRESULT hr;
TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count,
cache_req, huiaevent);
if (!huianode || !callback || !cache_req || !huiaevent)
return E_INVALIDARG;
if (!event_info)
WARN("No event information for event ID %d\n", event_id);
*huiaevent = NULL;
if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged))
{
FIXME("Property changed event registration currently unimplemented\n");
return E_NOTIMPL;
}
hr = UiaGetRuntimeId(huianode, &sa);
if (FAILED(hr))
return hr;
hr = create_uia_event(&event, event_id, scope, callback, sa);
if (FAILED(hr))
{
SafeArrayDestroy(sa);
return hr;
}
*huiaevent = (HUIAEVENT)event;
return S_OK;
}
......@@ -413,6 +413,17 @@ static const struct uia_event_info default_uia_events[] = {
EventArgsType_Simple, },
};
static const int event_id_idx[] = {
0x23, 0x1d, 0x0d, 0x0b, 0x19, 0x06, 0x07, 0x0f,
0x0a, 0x1e, 0x1b, 0x1a, 0x22, 0x00, 0x18, 0x10,
0x01, 0x20, 0x08, 0x17, 0x15, 0x13, 0x0e, 0x0c,
0x14, 0x09, 0x03, 0x21, 0x12, 0x16, 0x05, 0x1c,
0x02, 0x11, 0x04, 0x1f,
};
#define EVENT_ID_MIN 20000
#define EVENT_ID_MAX (EVENT_ID_MIN + ARRAY_SIZE(default_uia_events))
static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid)
{
struct uia_event_info *event;
......@@ -424,6 +435,14 @@ static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid)
return NULL;
}
const struct uia_event_info *uia_event_info_from_id(EVENTID event_id)
{
if ((event_id < EVENT_ID_MIN) || (event_id >= EVENT_ID_MAX))
return NULL;
return &default_uia_events[event_id_idx[event_id - EVENT_ID_MIN]];
}
/* Sorted by GUID. */
static const struct uia_pattern_info default_uia_patterns[] = {
{ &ScrollItem_Pattern_GUID, UIA_ScrollItemPatternId,
......
......@@ -147,6 +147,7 @@ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) 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;
const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) DECLSPEC_HIDDEN;
const struct uia_control_type_info *uia_control_type_info_from_id(CONTROLTYPEID control_type_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