Commit a3e71d4c authored by Connor McAdams's avatar Connor McAdams Committed by Alexandre Julliard

uiautomationcore/tests: Add tests for non-nested node events.

parent bfdda1c3
......@@ -73,6 +73,7 @@ DEFINE_EXPECT(prov_callback_base_hwnd);
DEFINE_EXPECT(prov_callback_nonclient);
DEFINE_EXPECT(prov_callback_proxy);
DEFINE_EXPECT(prov_callback_parent_proxy);
DEFINE_EXPECT(uia_event_callback);
DEFINE_EXPECT(winproc_GETOBJECT_UiaRoot);
DEFINE_EXPECT(child_winproc_GETOBJECT_UiaRoot);
DEFINE_EXPECT(Accessible_accNavigate);
......@@ -1133,6 +1134,7 @@ static struct Provider
IRawElementProviderFragment IRawElementProviderFragment_iface;
IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface;
IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface;
IRawElementProviderAdviseEvents IRawElementProviderAdviseEvents_iface;
IValueProvider IValueProvider_iface;
ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface;
LONG ref;
......@@ -1158,6 +1160,10 @@ static struct Provider
struct Provider_value_pattern_data value_pattern_data;
struct Provider_legacy_accessible_pattern_data legacy_acc_pattern_data;
IRawElementProviderFragment *focus_prov;
IRawElementProviderFragmentRoot **embedded_frag_roots;
int embedded_frag_roots_count;
int advise_events_added_event_id;
int advise_events_removed_event_id;
} Provider, Provider2, Provider_child, Provider_child2;
static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override;
static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links);
......@@ -1224,8 +1230,11 @@ enum {
FRAG_GET_RUNTIME_ID,
FRAG_GET_FRAGMENT_ROOT,
FRAG_GET_BOUNDING_RECT,
FRAG_GET_EMBEDDED_FRAGMENT_ROOTS,
FRAG_ROOT_GET_FOCUS,
HWND_OVERRIDE_GET_OVERRIDE_PROVIDER,
ADVISE_EVENTS_EVENT_ADDED,
ADVISE_EVENTS_EVENT_REMOVED,
};
static const char *prov_method_str[] = {
......@@ -1237,8 +1246,11 @@ static const char *prov_method_str[] = {
"GetRuntimeId",
"get_FragmentRoot",
"get_BoundingRectangle",
"GetEmbeddedFragmentRoots",
"GetFocus",
"GetOverrideProviderForHwnd",
"AdviseEventAdded",
"AdviseEventRemoved",
};
static const char *get_prov_method_str(int method)
......@@ -1276,6 +1288,15 @@ static struct prov_method_sequence *sequence;
{ prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
#define NODE_CREATE_SEQ_OPTIONAL(prov) \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \
/* Win10v1507 and below call this. */ \
{ prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \
{ prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, \
{ prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, \
{ prov , FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
/*
* This sequence of method calls is always used when creating an HUIANODE from
* an IRawElementProviderSimple that returns an HWND from get_HostRawElementProvider.
......@@ -1294,6 +1315,20 @@ static struct prov_method_sequence *sequence;
{ prov , FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
/*
* Node creation sequence with the first get_ProviderOptions call being optional.
* Used for event tests since each Windows version has different amounts of
* calls to get_ProviderOptions before node creation.
*/
#define NODE_CREATE_SEQ3(prov) \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \
/* Win10v1507 and below call this. */ \
{ prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \
{ prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \
{ prov , PROV_GET_PROPERTY_VALUE }, \
{ prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \
{ prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
static void flush_method_sequence(void)
{
HeapFree(GetProcessHeap(), 0, sequence);
......@@ -1593,6 +1628,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R
*ppv = &This->IRawElementProviderFragmentRoot_iface;
else if (IsEqualIID(riid, &IID_IRawElementProviderHwndOverride))
*ppv = &This->IRawElementProviderHwndOverride_iface;
else if (IsEqualIID(riid, &IID_IRawElementProviderAdviseEvents))
*ppv = &This->IRawElementProviderAdviseEvents_iface;
else if (IsEqualIID(riid, &IID_IValueProvider))
*ppv = &This->IValueProvider_iface;
else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider))
......@@ -2016,9 +2053,29 @@ static HRESULT WINAPI ProviderFragment_get_BoundingRectangle(IRawElementProvider
static HRESULT WINAPI ProviderFragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface,
SAFEARRAY **ret_val)
{
ok(0, "unexpected call\n");
struct Provider *This = impl_from_ProviderFragment(iface);
add_method_call(This, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
This->last_call_tid = GetCurrentThreadId();
*ret_val = NULL;
return E_NOTIMPL;
if (This->embedded_frag_roots && This->embedded_frag_roots_count)
{
SAFEARRAY *sa;
LONG idx;
if (!(sa = SafeArrayCreateVector(VT_UNKNOWN, 0, This->embedded_frag_roots_count)))
return E_FAIL;
for (idx = 0; idx < This->embedded_frag_roots_count; idx++)
SafeArrayPutElement(sa, &idx, (IUnknown *)This->embedded_frag_roots[idx]);
*ret_val = sa;
}
return S_OK;
}
static HRESULT WINAPI ProviderFragment_SetFocus(IRawElementProviderFragment *iface)
......@@ -2166,6 +2223,66 @@ static const IRawElementProviderHwndOverrideVtbl ProviderHwndOverrideVtbl = {
ProviderHwndOverride_GetOverrideProviderForHwnd,
};
static inline struct Provider *impl_from_ProviderAdviseEvents(IRawElementProviderAdviseEvents *iface)
{
return CONTAINING_RECORD(iface, struct Provider, IRawElementProviderAdviseEvents_iface);
}
static HRESULT WINAPI ProviderAdviseEvents_QueryInterface(IRawElementProviderAdviseEvents *iface, REFIID riid,
void **ppv)
{
struct Provider *Provider = impl_from_ProviderAdviseEvents(iface);
return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv);
}
static ULONG WINAPI ProviderAdviseEvents_AddRef(IRawElementProviderAdviseEvents *iface)
{
struct Provider *Provider = impl_from_ProviderAdviseEvents(iface);
return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface);
}
static ULONG WINAPI ProviderAdviseEvents_Release(IRawElementProviderAdviseEvents *iface)
{
struct Provider *Provider = impl_from_ProviderAdviseEvents(iface);
return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface);
}
static HRESULT WINAPI ProviderAdviseEvents_AdviseEventAdded(IRawElementProviderAdviseEvents *iface,
EVENTID event_id, SAFEARRAY *prop_ids)
{
struct Provider *This = impl_from_ProviderAdviseEvents(iface);
add_method_call(This, ADVISE_EVENTS_EVENT_ADDED);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
This->last_call_tid = GetCurrentThreadId();
This->advise_events_added_event_id = event_id;
return S_OK;
}
static HRESULT WINAPI ProviderAdviseEvents_AdviseEventRemoved(IRawElementProviderAdviseEvents *iface,
EVENTID event_id, SAFEARRAY *prop_ids)
{
struct Provider *This = impl_from_ProviderAdviseEvents(iface);
add_method_call(This, ADVISE_EVENTS_EVENT_REMOVED);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
This->last_call_tid = GetCurrentThreadId();
This->advise_events_removed_event_id = event_id;
return S_OK;
}
static const IRawElementProviderAdviseEventsVtbl ProviderAdviseEventsVtbl = {
ProviderAdviseEvents_QueryInterface,
ProviderAdviseEvents_AddRef,
ProviderAdviseEvents_Release,
ProviderAdviseEvents_AdviseEventAdded,
ProviderAdviseEvents_AdviseEventRemoved,
};
static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface)
{
return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface);
......@@ -2363,6 +2480,7 @@ static struct Provider Provider =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2379,6 +2497,7 @@ static struct Provider Provider2 =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2395,6 +2514,7 @@ static struct Provider Provider_child =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2411,6 +2531,7 @@ static struct Provider Provider_child2 =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2427,6 +2548,7 @@ static struct Provider Provider_hwnd =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2443,6 +2565,7 @@ static struct Provider Provider_nc =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2460,6 +2583,7 @@ static struct Provider Provider_proxy =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2477,6 +2601,7 @@ static struct Provider Provider_proxy2 =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2494,6 +2619,7 @@ static struct Provider Provider_override =
{ &ProviderFragmentVtbl },
{ &ProviderFragmentRootVtbl },
{ &ProviderHwndOverrideVtbl },
{ &ProviderAdviseEventsVtbl },
{ &ProviderValuePatternVtbl },
{ &ProviderLegacyIAccessiblePatternVtbl },
1,
......@@ -2512,6 +2638,7 @@ static struct Provider Provider_override =
{ &ProviderFragmentVtbl }, \
{ &ProviderFragmentRootVtbl }, \
{ &ProviderHwndOverrideVtbl }, \
{ &ProviderAdviseEventsVtbl }, \
{ &ProviderValuePatternVtbl }, \
{ &ProviderLegacyIAccessiblePatternVtbl }, \
1, \
......@@ -9491,6 +9618,9 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd,
memset(&prov->value_pattern_data, 0, sizeof(prov->value_pattern_data));
memset(&prov->legacy_acc_pattern_data, 0, sizeof(prov->legacy_acc_pattern_data));
prov->focus_prov = NULL;
prov->embedded_frag_roots = NULL;
prov->embedded_frag_roots_count = 0;
prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0;
if (initialize_nav_links)
{
prov->frag_root = NULL;
......@@ -13575,6 +13705,476 @@ static void test_UiaNodeFromFocus(void)
UnregisterClassA("UiaNodeFromFocus child class", NULL);
}
static const struct prov_method_sequence event_seq1[] = {
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win8+. */
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Only called on Arabic version of Win10+. */
{ 0 },
};
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, 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 },
{ 0 },
};
static const struct prov_method_sequence event_seq3[] = {
{ &Provider, PROV_GET_PROVIDER_OPTIONS },
/* The following four methods are only called on Arabic versions of Win10+. */
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL },
{ &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL },
{ &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */
{ &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL },
{ &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win8+. */
NODE_CREATE_SEQ3(&Provider),
{ &Provider, FRAG_GET_RUNTIME_ID },
{ 0 },
};
static const struct prov_method_sequence event_seq4[] = {
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ 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, 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 },
{ 0 },
};
static const struct prov_method_sequence event_seq6[] = {
{ &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO },
{ 0 },
};
static struct EventData {
LONG exp_lbound[2];
LONG exp_elems[2];
struct node_provider_desc exp_node_desc;
const WCHAR *exp_tree_struct;
} EventData;
static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1,
struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct)
{
EventData.exp_lbound[0] = exp_lbound0;
EventData.exp_lbound[1] = exp_lbound1;
EventData.exp_elems[0] = exp_elems0;
EventData.exp_elems[1] = exp_elems1;
if (exp_node_desc)
EventData.exp_node_desc = *exp_node_desc;
else
memset(&EventData.exp_node_desc, 0, sizeof(EventData.exp_node_desc));
EventData.exp_tree_struct = exp_tree_struct;
}
static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct)
{
CHECK_EXPECT(uia_event_callback);
if (!EventData.exp_elems[0] && !EventData.exp_elems[1])
ok(!req_data, "req_data != NULL\n");
else
test_cache_req_sa(req_data, EventData.exp_lbound, EventData.exp_elems, &EventData.exp_node_desc);
ok(!wcscmp(tree_struct, EventData.exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct));
}
struct event_test_thread_data {
HUIAEVENT event;
DWORD exp_thread_id;
};
static DWORD WINAPI uia_add_event_test_thread(LPVOID param)
{
struct event_test_thread_data *data = (struct event_test_thread_data *)param;
HRESULT hr;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = UiaRemoveEvent(data->event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
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,
"Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id);
CoUninitialize();
return 0;
}
static void test_UiaAddEvent(void)
{
IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface,
&Provider_child2.IRawElementProviderFragmentRoot_iface };
struct event_test_thread_data thread_data = { 0 };
struct Provider_prop_override prop_override;
struct node_provider_desc exp_node_desc;
struct UiaPropertyCondition prop_cond;
struct UiaCacheRequest cache_req;
HUIAEVENT event;
HUIANODE node;
HANDLE thread;
HRESULT hr;
HWND hwnd;
VARIANT v;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE);
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
provider_add_child(&Provider, &Provider_child);
initialize_provider(&Provider_child_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
provider_add_child(&Provider_child, &Provider_child_child);
/* Create a node with Provider as its only provider. */
method_sequences_enabled = FALSE;
hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL);
check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE);
VariantClear(&v);
/*
* Raise event without any registered event handlers.
*/
method_sequences_enabled = TRUE;
hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (SUCCEEDED(hr) && sequence_cnt)
ok_method_sequence(event_seq1, "event_seq1");
/*
* Register an event on a node without an HWND/RuntimeId. The event will
* be created successfully, but without any way to match a provider to
* this node, we won't be able to trigger the event handler.
*/
event = NULL;
Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface;
set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0,
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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq2, "event_seq2");
/*
* Even though we raise an event on the same provider as the one our node
* currently represents, without an HWND/RuntimeId, we have no way to
* match them. The event handler will not be called.
*/
hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (SUCCEEDED(hr) && sequence_cnt)
ok_method_sequence(event_seq3, "event_seq3");
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq4, "event_seq4");
/*
* Register an event on the same node again, except this time we have a
* runtimeID. Nodes returned to the event handler are normalized against
* the cache request view condition.
*/
event = NULL;
variant_init_bool(&v, TRUE);
set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None);
set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0,
AutomationElementMode_Full);
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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */
method_sequences_enabled = FALSE;
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE);
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
SET_EXPECT(uia_event_callback);
hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(uia_event_callback);
/*
* Runtime ID matches so event callback is invoked, but nothing matches
* the cache request view condition, so the callback will be passed a
* NULL cache request.
*/
variant_init_bool(&v, FALSE);
set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v);
set_provider_prop_override(&Provider, &prop_override, 1);
set_event_data(0, 0, 0, 0, &exp_node_desc, L"");
SET_EXPECT(uia_event_callback);
hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(uia_event_callback);
set_provider_prop_override(&Provider, NULL, 0);
method_sequences_enabled = TRUE;
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq4, "event_seq4");
/* 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");
todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq5, "event_seq5");
/*
* Only TreeScope_Children and not TreeScope_Element, handler won't be
* called.
*/
method_sequences_enabled = FALSE;
hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* Provider_child_child is not a direct child, handler won't be called. */
hr = UiaRaiseAutomationEvent(&Provider_child_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* Raised an event on Provider_child, handler will be called. */
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE);
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
SET_EXPECT(uia_event_callback);
hr = UiaRaiseAutomationEvent(&Provider_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(uia_event_callback);
/* Provider_child doesn't match the view condition, but Provider does. */
variant_init_bool(&v, FALSE);
set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v);
set_provider_prop_override(&Provider_child, &prop_override, 1);
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE);
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
SET_EXPECT(uia_event_callback);
hr = UiaRaiseAutomationEvent(&Provider_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(uia_event_callback);
set_provider_prop_override(&Provider_child, NULL, 0);
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
/* 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");
todo_wine 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);
add_provider_desc(&exp_node_desc, L"Main", L"Provider_child_child", TRUE);
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
SET_EXPECT(uia_event_callback);
hr = UiaRaiseAutomationEvent(&Provider_child_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
CoUninitialize();
/*
* When adding an event, each node provider's fragment root is queried
* for, and if one is retrieved, it's IRawElementProviderAdviseEvents
* interface is used. On Win10v1809+, ProviderOptions_UseComThreading is
* respected for these interfaces. Set Provider2 as the fragment root here
* to test this.
*/
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE);
initialize_provider(&Provider2, ProviderOptions_UseComThreading | ProviderOptions_ServerSideProvider, NULL, TRUE);
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(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);
todo_wine 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();
thread_data.event = event;
thread = CreateThread(NULL, 0, uia_add_event_test_thread, (void *)&thread_data, 0, NULL);
while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0)
{
MSG msg;
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
CloseHandle(thread);
/* Test retrieving AdviseEvents on embedded fragment roots. */
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE);
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE);
initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, FALSE);
Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface;
Provider.embedded_frag_roots = embedded_roots;
Provider.embedded_frag_roots_count = ARRAY_SIZE(embedded_roots);
Provider_child.frag_root = &Provider_child.IRawElementProviderFragmentRoot_iface;
Provider_child2.frag_root = &Provider_child2.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");
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);
todo_wine 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);
todo_wine ok(Provider_child2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId,
"Unexpected advise event added, event ID %d\n", Provider_child2.advise_events_added_event_id);
method_sequences_enabled = TRUE;
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
ok_method_sequence(event_seq6, "event_seq6");
UiaNodeRelease(node);
ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE);
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, TRUE);
hwnd = create_test_hwnd("UiaAddEvent test class");
UiaRegisterProviderCallback(test_uia_provider_callback);
/* Set clientside providers for our test window and the desktop. */
set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow());
Provider_proxy.frag_root = Provider_hwnd.frag_root = Provider_nc.frag_root = NULL;
base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface;
nc_prov = &Provider_nc.IRawElementProviderSimple_iface;
proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface;
set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd);
provider_add_child(&Provider_nc, &Provider_nc2);
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE);
Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface;
prov_root = &Provider.IRawElementProviderSimple_iface;
Provider.ignore_hwnd_prop = TRUE;
method_sequences_enabled = FALSE;
SET_EXPECT(prov_callback_base_hwnd);
SET_EXPECT(prov_callback_nonclient);
SET_EXPECT(prov_callback_proxy);
hr = UiaGetRootNode(&node);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!node, "Node == NULL.\n");
ok(Provider_proxy.ref == 2, "Unexpected refcnt %ld\n", Provider_proxy.ref);
ok(Provider_hwnd.ref == 2, "Unexpected refcnt %ld\n", Provider_hwnd.ref);
ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref);
CHECK_CALLED(prov_callback_base_hwnd);
CHECK_CALLED(prov_callback_nonclient);
CHECK_CALLED(prov_callback_proxy);
/* Register an event on the desktop HWND. */
set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0,
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");
/*
* Raising an event on a serverside provider results in no clientside
* providers being added for the HWND, which means we won't match this
* event.
*/
Provider_hwnd2.prov_opts = ProviderOptions_ServerSideProvider;
hr = UiaRaiseAutomationEvent(&Provider_hwnd2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* Now that Provider has clientside providers, the event will be matched. */
SET_EXPECT(winproc_GETOBJECT_UiaRoot);
SET_EXPECT(prov_callback_base_hwnd);
SET_EXPECT(prov_callback_nonclient);
SET_EXPECT_MULTI(prov_callback_proxy, 2);
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd);
add_provider_desc(&exp_node_desc, L"Main", L"Provider", FALSE);
add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", TRUE);
add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE);
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
SET_EXPECT(uia_event_callback);
Provider_hwnd2.prov_opts = ProviderOptions_ClientSideProvider;
hr = UiaRaiseAutomationEvent(&Provider_hwnd2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
todo_wine CHECK_CALLED(prov_callback_base_hwnd);
todo_wine CHECK_CALLED(prov_callback_nonclient);
todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2);
todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
UiaNodeRelease(node);
method_sequences_enabled = TRUE;
CoUninitialize();
UiaRegisterProviderCallback(NULL);
DestroyWindow(hwnd);
UnregisterClassA("UiaAddEvent test class", NULL);
}
/*
* Once a process returns a UI Automation provider with
* UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This
......@@ -13645,6 +14245,7 @@ START_TEST(uiautomation)
test_default_clientside_providers();
test_UiaGetRootNode();
test_UiaNodeFromFocus();
test_UiaAddEvent();
if (uia_dll)
{
pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
......
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