uiautomationcore/tests: Add tests for IProxyProviderWinEventHandler based MSAA to UIA event bridging.
Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
parent
ed2bfd4ad7
commit
87af89cb3b
1 changed files with 655 additions and 20 deletions
|
@ -391,6 +391,7 @@ DEFINE_EXPECT(prov_callback_nonclient);
|
||||||
DEFINE_EXPECT(prov_callback_proxy);
|
DEFINE_EXPECT(prov_callback_proxy);
|
||||||
DEFINE_EXPECT(prov_callback_parent_proxy);
|
DEFINE_EXPECT(prov_callback_parent_proxy);
|
||||||
DEFINE_EXPECT(uia_event_callback);
|
DEFINE_EXPECT(uia_event_callback);
|
||||||
|
DEFINE_EXPECT(uia_event_callback2);
|
||||||
DEFINE_EXPECT(uia_com_event_callback);
|
DEFINE_EXPECT(uia_com_event_callback);
|
||||||
DEFINE_EXPECT(winproc_GETOBJECT_UiaRoot);
|
DEFINE_EXPECT(winproc_GETOBJECT_UiaRoot);
|
||||||
DEFINE_EXPECT(child_winproc_GETOBJECT_UiaRoot);
|
DEFINE_EXPECT(child_winproc_GETOBJECT_UiaRoot);
|
||||||
|
@ -493,6 +494,34 @@ static void test_implements_interface_(IUnknown *unk, const GUID *iid, BOOL exp_
|
||||||
DEFINE_ACC_METHOD_EXPECT(get_locale); \
|
DEFINE_ACC_METHOD_EXPECT(get_locale); \
|
||||||
DEFINE_ACC_METHOD_EXPECT(get_attributes) \
|
DEFINE_ACC_METHOD_EXPECT(get_attributes) \
|
||||||
|
|
||||||
|
static DWORD msg_wait_for_all_events(HANDLE *event_handles, int event_handle_count, DWORD timeout_val)
|
||||||
|
{
|
||||||
|
int events_handled = 0;
|
||||||
|
DWORD wait_res;
|
||||||
|
|
||||||
|
while ((wait_res = MsgWaitForMultipleObjects(event_handle_count, (const HANDLE *)event_handles, FALSE, timeout_val,
|
||||||
|
QS_ALLINPUT)) <= (WAIT_OBJECT_0 + event_handle_count))
|
||||||
|
{
|
||||||
|
if (wait_res == (WAIT_OBJECT_0 + event_handle_count))
|
||||||
|
{
|
||||||
|
MSG msg;
|
||||||
|
|
||||||
|
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
|
||||||
|
{
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
events_handled++;
|
||||||
|
|
||||||
|
if (events_handled == event_handle_count)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wait_res;
|
||||||
|
}
|
||||||
|
|
||||||
static struct Accessible
|
static struct Accessible
|
||||||
{
|
{
|
||||||
IAccessible IAccessible_iface;
|
IAccessible IAccessible_iface;
|
||||||
|
@ -1555,6 +1584,19 @@ struct Provider_legacy_accessible_pattern_data
|
||||||
DWORD role;
|
DWORD role;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Provider_win_event_handler_data
|
||||||
|
{
|
||||||
|
BOOL is_supported;
|
||||||
|
|
||||||
|
DWORD exp_win_event_id;
|
||||||
|
HWND exp_win_event_hwnd;
|
||||||
|
LONG exp_win_event_obj_id;
|
||||||
|
LONG exp_win_event_child_id;
|
||||||
|
|
||||||
|
IRawElementProviderSimple *responder_prov;
|
||||||
|
int responder_event;
|
||||||
|
};
|
||||||
|
|
||||||
static struct Provider
|
static struct Provider
|
||||||
{
|
{
|
||||||
IRawElementProviderSimple IRawElementProviderSimple_iface;
|
IRawElementProviderSimple IRawElementProviderSimple_iface;
|
||||||
|
@ -1562,6 +1604,7 @@ static struct Provider
|
||||||
IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface;
|
IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface;
|
||||||
IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface;
|
IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface;
|
||||||
IRawElementProviderAdviseEvents IRawElementProviderAdviseEvents_iface;
|
IRawElementProviderAdviseEvents IRawElementProviderAdviseEvents_iface;
|
||||||
|
IProxyProviderWinEventHandler IProxyProviderWinEventHandler_iface;
|
||||||
IValueProvider IValueProvider_iface;
|
IValueProvider IValueProvider_iface;
|
||||||
ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface;
|
ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface;
|
||||||
LONG ref;
|
LONG ref;
|
||||||
|
@ -1591,6 +1634,7 @@ static struct Provider
|
||||||
int embedded_frag_roots_count;
|
int embedded_frag_roots_count;
|
||||||
int advise_events_added_event_id;
|
int advise_events_added_event_id;
|
||||||
int advise_events_removed_event_id;
|
int advise_events_removed_event_id;
|
||||||
|
struct Provider_win_event_handler_data win_event_handler_data;
|
||||||
} Provider, Provider2, Provider_child, Provider_child2;
|
} Provider, Provider2, Provider_child, Provider_child2;
|
||||||
static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override;
|
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);
|
static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links);
|
||||||
|
@ -1662,6 +1706,7 @@ enum {
|
||||||
HWND_OVERRIDE_GET_OVERRIDE_PROVIDER,
|
HWND_OVERRIDE_GET_OVERRIDE_PROVIDER,
|
||||||
ADVISE_EVENTS_EVENT_ADDED,
|
ADVISE_EVENTS_EVENT_ADDED,
|
||||||
ADVISE_EVENTS_EVENT_REMOVED,
|
ADVISE_EVENTS_EVENT_REMOVED,
|
||||||
|
WINEVENT_HANDLER_RESPOND_TO_WINEVENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *prov_method_str[] = {
|
static const char *prov_method_str[] = {
|
||||||
|
@ -1678,6 +1723,7 @@ static const char *prov_method_str[] = {
|
||||||
"GetOverrideProviderForHwnd",
|
"GetOverrideProviderForHwnd",
|
||||||
"AdviseEventAdded",
|
"AdviseEventAdded",
|
||||||
"AdviseEventRemoved",
|
"AdviseEventRemoved",
|
||||||
|
"RespondToWinEvent",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *get_prov_method_str(int method)
|
static const char *get_prov_method_str(int method)
|
||||||
|
@ -2061,6 +2107,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R
|
||||||
*ppv = &This->IValueProvider_iface;
|
*ppv = &This->IValueProvider_iface;
|
||||||
else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider))
|
else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider))
|
||||||
*ppv = &This->ILegacyIAccessibleProvider_iface;
|
*ppv = &This->ILegacyIAccessibleProvider_iface;
|
||||||
|
else if (This->win_event_handler_data.is_supported && IsEqualIID(riid, &IID_IProxyProviderWinEventHandler))
|
||||||
|
*ppv = &This->IProxyProviderWinEventHandler_iface;
|
||||||
else
|
else
|
||||||
return E_NOINTERFACE;
|
return E_NOINTERFACE;
|
||||||
|
|
||||||
|
@ -2723,6 +2771,64 @@ static const IRawElementProviderAdviseEventsVtbl ProviderAdviseEventsVtbl = {
|
||||||
ProviderAdviseEvents_AdviseEventRemoved,
|
ProviderAdviseEvents_AdviseEventRemoved,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct Provider *impl_from_ProviderWinEventHandler(IProxyProviderWinEventHandler *iface)
|
||||||
|
{
|
||||||
|
return CONTAINING_RECORD(iface, struct Provider, IProxyProviderWinEventHandler_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI ProviderWinEventHandler_QueryInterface(IProxyProviderWinEventHandler *iface, REFIID riid,
|
||||||
|
void **ppv)
|
||||||
|
{
|
||||||
|
struct Provider *Provider = impl_from_ProviderWinEventHandler(iface);
|
||||||
|
return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI ProviderWinEventHandler_AddRef(IProxyProviderWinEventHandler *iface)
|
||||||
|
{
|
||||||
|
struct Provider *Provider = impl_from_ProviderWinEventHandler(iface);
|
||||||
|
return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI ProviderWinEventHandler_Release(IProxyProviderWinEventHandler *iface)
|
||||||
|
{
|
||||||
|
struct Provider *Provider = impl_from_ProviderWinEventHandler(iface);
|
||||||
|
return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI ProviderWinEventHandler_RespondToWinEvent(IProxyProviderWinEventHandler *iface,
|
||||||
|
DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, IProxyProviderWinEventSink *event_sink)
|
||||||
|
{
|
||||||
|
struct Provider *This = impl_from_ProviderWinEventHandler(iface);
|
||||||
|
struct Provider_win_event_handler_data *data;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
PROV_METHOD_TRACE(This, RespondToWinEvent);
|
||||||
|
data = &This->win_event_handler_data;
|
||||||
|
if ((data->exp_win_event_id != event_id) || (data->exp_win_event_hwnd != hwnd) || (data->exp_win_event_obj_id != obj_id) ||
|
||||||
|
(data->exp_win_event_child_id != child_id))
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
add_method_call(This, WINEVENT_HANDLER_RESPOND_TO_WINEVENT);
|
||||||
|
if (This->expected_tid)
|
||||||
|
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
|
||||||
|
This->last_call_tid = GetCurrentThreadId();
|
||||||
|
|
||||||
|
if (data->responder_prov)
|
||||||
|
{
|
||||||
|
hr = IProxyProviderWinEventSink_AddAutomationEvent(event_sink, data->responder_prov, data->responder_event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const IProxyProviderWinEventHandlerVtbl ProviderWinEventHandlerVtbl = {
|
||||||
|
ProviderWinEventHandler_QueryInterface,
|
||||||
|
ProviderWinEventHandler_AddRef,
|
||||||
|
ProviderWinEventHandler_Release,
|
||||||
|
ProviderWinEventHandler_RespondToWinEvent,
|
||||||
|
};
|
||||||
|
|
||||||
static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface)
|
static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface)
|
||||||
{
|
{
|
||||||
return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface);
|
return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface);
|
||||||
|
@ -2921,6 +3027,7 @@ static struct Provider Provider =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -2938,6 +3045,7 @@ static struct Provider Provider2 =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -2955,6 +3063,7 @@ static struct Provider Provider_child =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -2972,6 +3081,7 @@ static struct Provider Provider_child2 =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -2989,6 +3099,7 @@ static struct Provider Provider_hwnd =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -3006,6 +3117,7 @@ static struct Provider Provider_nc =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -3024,6 +3136,7 @@ static struct Provider Provider_proxy =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -3042,6 +3155,7 @@ static struct Provider Provider_proxy2 =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -3060,6 +3174,7 @@ static struct Provider Provider_override =
|
||||||
{ &ProviderFragmentRootVtbl },
|
{ &ProviderFragmentRootVtbl },
|
||||||
{ &ProviderHwndOverrideVtbl },
|
{ &ProviderHwndOverrideVtbl },
|
||||||
{ &ProviderAdviseEventsVtbl },
|
{ &ProviderAdviseEventsVtbl },
|
||||||
|
{ &ProviderWinEventHandlerVtbl },
|
||||||
{ &ProviderValuePatternVtbl },
|
{ &ProviderValuePatternVtbl },
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl },
|
{ &ProviderLegacyIAccessiblePatternVtbl },
|
||||||
1,
|
1,
|
||||||
|
@ -3079,6 +3194,7 @@ static struct Provider Provider_override =
|
||||||
{ &ProviderFragmentRootVtbl }, \
|
{ &ProviderFragmentRootVtbl }, \
|
||||||
{ &ProviderHwndOverrideVtbl }, \
|
{ &ProviderHwndOverrideVtbl }, \
|
||||||
{ &ProviderAdviseEventsVtbl }, \
|
{ &ProviderAdviseEventsVtbl }, \
|
||||||
|
{ &ProviderWinEventHandlerVtbl }, \
|
||||||
{ &ProviderValuePatternVtbl }, \
|
{ &ProviderValuePatternVtbl }, \
|
||||||
{ &ProviderLegacyIAccessiblePatternVtbl }, \
|
{ &ProviderLegacyIAccessiblePatternVtbl }, \
|
||||||
1, \
|
1, \
|
||||||
|
@ -10793,6 +10909,37 @@ static void set_property_override(struct Provider_prop_override *override, int p
|
||||||
override->val = *val;
|
override->val = *val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_provider_win_event_handler_respond_prov(struct Provider *prov, IRawElementProviderSimple *responder_prov,
|
||||||
|
int responder_event)
|
||||||
|
{
|
||||||
|
struct Provider_win_event_handler_data *data = &prov->win_event_handler_data;
|
||||||
|
|
||||||
|
data->responder_prov = responder_prov;
|
||||||
|
data->responder_event = responder_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_provider_win_event_handler_win_event_expects(struct Provider *prov, DWORD exp_win_event_id,
|
||||||
|
HWND exp_win_event_hwnd, LONG exp_win_event_obj_id, LONG exp_win_event_child_id)
|
||||||
|
{
|
||||||
|
struct Provider_win_event_handler_data *data = &prov->win_event_handler_data;
|
||||||
|
|
||||||
|
data->exp_win_event_id = exp_win_event_id;
|
||||||
|
data->exp_win_event_hwnd = exp_win_event_hwnd;
|
||||||
|
data->exp_win_event_obj_id = exp_win_event_obj_id;
|
||||||
|
data->exp_win_event_child_id = exp_win_event_child_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_provider_runtime_id(struct Provider *prov, int val, int val2)
|
||||||
|
{
|
||||||
|
prov->runtime_id[0] = val;
|
||||||
|
prov->runtime_id[1] = val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_provider_advise_events_ids(struct Provider *prov)
|
||||||
|
{
|
||||||
|
prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links)
|
static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links)
|
||||||
{
|
{
|
||||||
prov->prov_opts = prov_opts;
|
prov->prov_opts = prov_opts;
|
||||||
|
@ -10812,6 +10959,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd,
|
||||||
prov->embedded_frag_roots = NULL;
|
prov->embedded_frag_roots = NULL;
|
||||||
prov->embedded_frag_roots_count = 0;
|
prov->embedded_frag_roots_count = 0;
|
||||||
prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0;
|
prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0;
|
||||||
|
memset(&prov->win_event_handler_data, 0, sizeof(prov->win_event_handler_data));
|
||||||
if (initialize_nav_links)
|
if (initialize_nav_links)
|
||||||
{
|
{
|
||||||
prov->frag_root = NULL;
|
prov->frag_root = NULL;
|
||||||
|
@ -11875,6 +12023,14 @@ static HWND create_child_test_hwnd(const char *class_name, HWND parent)
|
||||||
0, 0, 50, 50, parent, NULL, NULL, NULL);
|
0, 0, 50, 50, parent, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void destroy_test_hwnd(HWND hwnd, const char *class_name, const char *child_class_name)
|
||||||
|
{
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
UnregisterClassA(class_name, NULL);
|
||||||
|
if (child_class_name)
|
||||||
|
UnregisterClassA(child_class_name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd, BOOL block_hwnd_provs)
|
static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd, BOOL block_hwnd_provs)
|
||||||
{
|
{
|
||||||
IUIAutomationElement *element;
|
IUIAutomationElement *element;
|
||||||
|
@ -14164,6 +14320,17 @@ static void check_uia_hwnd_expects_at_most(int proxy_cback_count, int base_hwnd_
|
||||||
CHECK_CALLED_AT_MOST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count);
|
CHECK_CALLED_AT_MOST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_uia_hwnd_expects_at_least(int proxy_cback_count, BOOL proxy_cback_todo,
|
||||||
|
int base_hwnd_cback_count, BOOL base_hwnd_cback_todo, int nc_cback_count, BOOL nc_cback_todo,
|
||||||
|
int win_get_uia_obj_count, BOOL win_get_uia_obj_todo, int win_get_client_obj_count, BOOL win_get_client_obj_todo)
|
||||||
|
{
|
||||||
|
todo_wine_if(proxy_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_proxy, proxy_cback_count);
|
||||||
|
todo_wine_if(base_hwnd_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_base_hwnd, base_hwnd_cback_count);
|
||||||
|
todo_wine_if(nc_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_nonclient, nc_cback_count);
|
||||||
|
todo_wine_if(win_get_uia_obj_todo) CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_UiaRoot, win_get_uia_obj_count);
|
||||||
|
todo_wine_if(win_get_client_obj_todo) CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count);
|
||||||
|
}
|
||||||
|
|
||||||
static struct ComEventData {
|
static struct ComEventData {
|
||||||
struct node_provider_desc exp_node_desc;
|
struct node_provider_desc exp_node_desc;
|
||||||
struct node_provider_desc exp_nested_node_desc;
|
struct node_provider_desc exp_nested_node_desc;
|
||||||
|
@ -15551,47 +15718,67 @@ static struct EventData {
|
||||||
|
|
||||||
struct node_provider_desc exp_nested_node_desc;
|
struct node_provider_desc exp_nested_node_desc;
|
||||||
HANDLE event_handle;
|
HANDLE event_handle;
|
||||||
} EventData;
|
} EventData, EventData2;
|
||||||
|
|
||||||
static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1,
|
static void set_event_data_struct(struct EventData *data, LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0,
|
||||||
struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct)
|
LONG exp_elems1, struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct)
|
||||||
{
|
{
|
||||||
EventData.exp_lbound[0] = exp_lbound0;
|
data->exp_lbound[0] = exp_lbound0;
|
||||||
EventData.exp_lbound[1] = exp_lbound1;
|
data->exp_lbound[1] = exp_lbound1;
|
||||||
EventData.exp_elems[0] = exp_elems0;
|
data->exp_elems[0] = exp_elems0;
|
||||||
EventData.exp_elems[1] = exp_elems1;
|
data->exp_elems[1] = exp_elems1;
|
||||||
if (exp_node_desc)
|
if (exp_node_desc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
EventData.exp_node_desc = *exp_node_desc;
|
data->exp_node_desc = *exp_node_desc;
|
||||||
for (i = 0; i < exp_node_desc->prov_count; i++)
|
for (i = 0; i < exp_node_desc->prov_count; i++)
|
||||||
{
|
{
|
||||||
if (exp_node_desc->nested_desc[i])
|
if (exp_node_desc->nested_desc[i])
|
||||||
{
|
{
|
||||||
EventData.exp_nested_node_desc = *exp_node_desc->nested_desc[i];
|
data->exp_nested_node_desc = *exp_node_desc->nested_desc[i];
|
||||||
EventData.exp_node_desc.nested_desc[i] = &EventData.exp_nested_node_desc;
|
data->exp_node_desc.nested_desc[i] = &data->exp_nested_node_desc;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memset(&EventData.exp_node_desc, 0, sizeof(EventData.exp_node_desc));
|
memset(&data->exp_node_desc, 0, sizeof(data->exp_node_desc));
|
||||||
EventData.exp_tree_struct = exp_tree_struct;
|
data->exp_tree_struct = exp_tree_struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
set_event_data_struct(&EventData, exp_lbound0, exp_lbound1, exp_elems0, exp_elems1, exp_node_desc,
|
||||||
|
exp_tree_struct);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define check_event_data( data, args, req_data, tree_struct ) \
|
||||||
|
check_event_data_( (data), (args), (req_data), (tree_struct), __FILE__, __LINE__)
|
||||||
|
static void check_event_data_(struct EventData *data, struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct,
|
||||||
|
const char *file, int line)
|
||||||
|
{
|
||||||
|
if (!data->exp_elems[0] && !data->exp_elems[1])
|
||||||
|
ok(!req_data, "req_data != NULL\n");
|
||||||
|
else
|
||||||
|
test_cache_req_sa_(req_data, data->exp_lbound, data->exp_elems, &data->exp_node_desc, file, line);
|
||||||
|
|
||||||
|
ok(!wcscmp(tree_struct, data->exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct));
|
||||||
|
if (data->event_handle)
|
||||||
|
SetEvent(data->event_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct)
|
static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct)
|
||||||
{
|
{
|
||||||
CHECK_EXPECT(uia_event_callback);
|
CHECK_EXPECT(uia_event_callback);
|
||||||
|
check_event_data(&EventData, args, req_data, tree_struct);
|
||||||
|
}
|
||||||
|
|
||||||
if (!EventData.exp_elems[0] && !EventData.exp_elems[1])
|
static void WINAPI uia_event_callback2(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct)
|
||||||
ok(!req_data, "req_data != NULL\n");
|
{
|
||||||
else
|
CHECK_EXPECT(uia_event_callback2);
|
||||||
test_cache_req_sa(req_data, EventData.exp_lbound, EventData.exp_elems, &EventData.exp_node_desc);
|
check_event_data(&EventData2, args, req_data, tree_struct);
|
||||||
|
|
||||||
ok(!wcscmp(tree_struct, EventData.exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct));
|
|
||||||
if (EventData.event_handle)
|
|
||||||
SetEvent(EventData.event_handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -16516,6 +16703,453 @@ static void test_UiaHasServerSideProvider(void)
|
||||||
UnregisterClassA("UiaHasServerSideProvider test class", NULL);
|
UnregisterClassA("UiaHasServerSideProvider test class", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct prov_method_sequence win_event_handler_seq[] = {
|
||||||
|
{ &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO },
|
||||||
|
{ &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */
|
||||||
|
{ &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */
|
||||||
|
{ &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */
|
||||||
|
{ &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT },
|
||||||
|
{ &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT },
|
||||||
|
NODE_CREATE_SEQ(&Provider_child),
|
||||||
|
{ &Provider_child, FRAG_GET_RUNTIME_ID },
|
||||||
|
{ &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define test_uia_event_win_event_mapping( win_event, hwnd, obj_id, child_id, event_handles, event_handle_count, \
|
||||||
|
expect_event1, expect_event2, todo ) \
|
||||||
|
test_uia_event_win_event_mapping_( (win_event), (hwnd), (obj_id), (child_id), (event_handles), (event_handle_count), \
|
||||||
|
(expect_event1), (expect_event2), (todo), __FILE__, __LINE__)
|
||||||
|
static void test_uia_event_win_event_mapping_(DWORD win_event, HWND hwnd, LONG obj_id, LONG child_id,
|
||||||
|
HANDLE *event_handles, int event_handle_count, BOOL expect_event1, BOOL expect_event2,
|
||||||
|
BOOL todo, const char *file, int line)
|
||||||
|
{
|
||||||
|
const BOOL exp_timeout = (!expect_event1 && !expect_event2);
|
||||||
|
DWORD timeout_val = exp_timeout ? 500 : 3000;
|
||||||
|
DWORD wait_res;
|
||||||
|
|
||||||
|
SET_EXPECT_MULTI(uia_event_callback, !!expect_event1);
|
||||||
|
SET_EXPECT_MULTI(uia_event_callback2, !!expect_event2);
|
||||||
|
if (expect_event2)
|
||||||
|
SET_EXPECT(uia_event_callback2);
|
||||||
|
NotifyWinEvent(win_event, hwnd, obj_id, child_id);
|
||||||
|
|
||||||
|
wait_res = msg_wait_for_all_events(event_handles, event_handle_count, timeout_val);
|
||||||
|
todo_wine_if(todo) ok_(file, line)((wait_res == WAIT_TIMEOUT) == exp_timeout,
|
||||||
|
"Unexpected result while waiting for event callback(s).\n");
|
||||||
|
if (expect_event1)
|
||||||
|
todo_wine_if(todo) CHECK_CALLED(uia_event_callback);
|
||||||
|
if (expect_event2)
|
||||||
|
todo_wine_if(todo) CHECK_CALLED(uia_event_callback2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define test_provider_event_advise_added( prov, event_id, todo) \
|
||||||
|
test_provider_event_advise_added_( (prov), (event_id), (todo), __FILE__, __LINE__)
|
||||||
|
static void test_provider_event_advise_added_(struct Provider *prov, int event_id, BOOL todo, const char *file, int line)
|
||||||
|
{
|
||||||
|
todo_wine_if (todo) ok_(file, line)(prov->advise_events_added_event_id == event_id, "%s: Unexpected advise event added, event ID %d.\n",
|
||||||
|
prov->prov_name, prov->advise_events_added_event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID param)
|
||||||
|
{
|
||||||
|
struct UiaCacheRequest cache_req = { (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0,
|
||||||
|
AutomationElementMode_Full };
|
||||||
|
HWND hwnd[2] = { ((HWND *)param)[0], ((HWND *)param)[1] };
|
||||||
|
struct node_provider_desc exp_node_desc;
|
||||||
|
HUIAEVENT event, event2;
|
||||||
|
HANDLE event_handles[2];
|
||||||
|
HWND tmp_hwnd;
|
||||||
|
HUIANODE node;
|
||||||
|
HRESULT hr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
method_sequences_enabled = FALSE;
|
||||||
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(event_handles); i++)
|
||||||
|
event_handles[i] = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 2, 0);
|
||||||
|
hr = UiaNodeFromHandle(hwnd[0], &node);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
ok(!!node, "Node == NULL.\n");
|
||||||
|
check_uia_hwnd_expects_at_most(1, 1, 1, 2, 0);
|
||||||
|
|
||||||
|
set_uia_hwnd_expects(2, 2, 2, 4, 0);
|
||||||
|
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req,
|
||||||
|
&event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
/* Windows 11 recreates HWND clientside providers for the node passed into UiaAddEvent. */
|
||||||
|
check_uia_hwnd_expects_at_most(2, 2, 2, 4, 0);
|
||||||
|
|
||||||
|
set_uia_hwnd_expects(2, 2, 2, 4, 0);
|
||||||
|
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback2, TreeScope_Subtree, NULL, 0, &cache_req,
|
||||||
|
&event2);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
/* Windows 11 recreates HWND clientside providers for the node passed into UiaAddEvent. */
|
||||||
|
check_uia_hwnd_expects_at_most(2, 2, 2, 4, 0);
|
||||||
|
UiaNodeRelease(node);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raise EVENT_OBJECT_FOCUS. If none of our clientside providers returned
|
||||||
|
* an IProxyProviderWinEventHandler interface when being advised of events
|
||||||
|
* being listened for, nothing happens.
|
||||||
|
*/
|
||||||
|
EventData.event_handle = event_handles[0];
|
||||||
|
EventData2.event_handle = event_handles[1];
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
ARRAY_SIZE(event_handles), FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return an IProxyProviderWinEventHandler interface on our clientside
|
||||||
|
* providers. WinEvents will now be listened for. If a provider returns a
|
||||||
|
* WinEvent handler interface, IRawElementProviderAdviseEvents will not be
|
||||||
|
* queried for or used.
|
||||||
|
*/
|
||||||
|
initialize_provider_advise_events_ids(&Provider_hwnd2);
|
||||||
|
initialize_provider_advise_events_ids(&Provider_nc2);
|
||||||
|
Provider_hwnd2.win_event_handler_data.is_supported = Provider_nc2.win_event_handler_data.is_supported = TRUE;
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 2, 0);
|
||||||
|
hr = UiaEventAddWindow(event, hwnd[0]);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
test_provider_event_advise_added(&Provider_hwnd2, 0, TRUE);
|
||||||
|
test_provider_event_advise_added(&Provider_nc2, 0, TRUE);
|
||||||
|
check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WinEvents will now be listened for, however if our HWND has a
|
||||||
|
* serverside provider they will be ignored.
|
||||||
|
*/
|
||||||
|
SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 2); /* Only called twice on Win11. */
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get rid of our serverside provider and raise EVENT_OBJECT_FOCUS
|
||||||
|
* again. Now, our WinEvent handler interfaces will be invoked.
|
||||||
|
*/
|
||||||
|
prov_root = NULL;
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_hwnd2, EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_nc2, EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
|
||||||
|
initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE);
|
||||||
|
set_provider_runtime_id(&Provider_child, UIA_RUNTIME_ID_PREFIX, HandleToUlong(hwnd[0]));
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_hwnd2, &Provider_child.IRawElementProviderSimple_iface,
|
||||||
|
UIA_AutomationFocusChangedEventId);
|
||||||
|
|
||||||
|
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)");
|
||||||
|
|
||||||
|
method_sequences_enabled = TRUE;
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 4, 3);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
if (CALLED_COUNT(winproc_GETOBJECT_CLIENT))
|
||||||
|
ok_method_sequence(win_event_handler_seq, "win_event_handler_seq");
|
||||||
|
check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE);
|
||||||
|
method_sequences_enabled = FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not all WinEvents are passed to our WinEvent responder interface -
|
||||||
|
* they're filtered by HWND.
|
||||||
|
*/
|
||||||
|
Provider_hwnd.win_event_handler_data.is_supported = Provider_nc.win_event_handler_data.is_supported = TRUE;
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_nc, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_nc, &Provider_child.IRawElementProviderSimple_iface,
|
||||||
|
UIA_AutomationFocusChangedEventId);
|
||||||
|
SET_EXPECT(uia_event_callback);
|
||||||
|
set_uia_hwnd_expects(0, 1, 1, 0, 0);
|
||||||
|
NotifyWinEvent(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
if (msg_wait_for_all_events(event_handles, 1, 3000) == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
win_skip("Win10v1507 and below don't filter WinEvents by HWND, skipping further tests.\n");
|
||||||
|
|
||||||
|
CHECK_CALLED(uia_event_callback);
|
||||||
|
check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
|
||||||
|
hr = UiaRemoveEvent(event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event2);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
goto skip_win_event_hwnd_filter_test;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear expects/called values. */
|
||||||
|
CHECK_CALLED_MULTI(uia_event_callback, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Child HWNDs of top level HWNDs that are within our scope are listened
|
||||||
|
* to by default.
|
||||||
|
*/
|
||||||
|
child_win_prov_root = NULL;
|
||||||
|
Provider_hwnd3.win_event_handler_data.is_supported = Provider_nc3.win_event_handler_data.is_supported = TRUE;
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_nc3, EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_hwnd3, EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_nc3, &Provider_child.IRawElementProviderSimple_iface,
|
||||||
|
UIA_AutomationFocusChangedEventId);
|
||||||
|
|
||||||
|
set_uia_hwnd_expects(0, 1, 1, 2, 0);
|
||||||
|
SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Only sent 4 times on Win11. */
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE);
|
||||||
|
todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Child HWND now has a serverside provider, WinEvent is ignored.
|
||||||
|
*/
|
||||||
|
child_win_prov_root = &Provider2.IRawElementProviderSimple_iface;
|
||||||
|
|
||||||
|
SET_EXPECT(winproc_GETOBJECT_UiaRoot); /* Only sent on Win11. */
|
||||||
|
SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); /* Only sent 2 times on Win11. */
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
|
||||||
|
CHECK_CALLED_AT_MOST(winproc_GETOBJECT_UiaRoot, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HWNDs owned by a top level HWND that is within our scope are ignored.
|
||||||
|
*/
|
||||||
|
child_win_prov_root = NULL;
|
||||||
|
tmp_hwnd = CreateWindowA("ProxyProviderWinEventHandler test child class", "Test child window 2", WS_POPUP,
|
||||||
|
0, 0, 50, 50, hwnd[0], NULL, NULL, NULL);
|
||||||
|
Provider_nc3.hwnd = Provider_hwnd3.hwnd = tmp_hwnd;
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, tmp_hwnd, OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
DestroyWindow(tmp_hwnd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add our test child HWND to event2. This only puts the child HWND within
|
||||||
|
* the scope of event2, it doesn't put the parent HWND within its scope.
|
||||||
|
*/
|
||||||
|
child_win_prov_root = &Provider2.IRawElementProviderSimple_iface;
|
||||||
|
Provider_nc3.hwnd = Provider_hwnd3.hwnd = hwnd[1];
|
||||||
|
SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); /* Only sent 2 times on Win11. */
|
||||||
|
set_uia_hwnd_expects(0, 1, 1, 1, 0);
|
||||||
|
hr = UiaEventAddWindow(event2, hwnd[1]);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE);
|
||||||
|
CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raise a WinEvent on our top level test HWND, will not invoke the
|
||||||
|
* callback on event2.
|
||||||
|
*/
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 4, 3);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE);
|
||||||
|
|
||||||
|
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */
|
||||||
|
child_win_prov_root = NULL;
|
||||||
|
set_event_data_struct(&EventData2, 0, 0, 1, 1, &exp_node_desc, L"P)");
|
||||||
|
|
||||||
|
set_uia_hwnd_expects(0, 2, 2, 4, 0);
|
||||||
|
SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 8); /* Only sent 8 times on Win11. */
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE);
|
||||||
|
todo_wine CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2);
|
||||||
|
check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 2, TRUE, 0, FALSE);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event2);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an event on the desktop HWND. If a WinEvent handler interface is
|
||||||
|
* returned on a provider representing the desktop HWND, all visible
|
||||||
|
* top-level HWNDs at the time of advisement will be considered within
|
||||||
|
* scope.
|
||||||
|
*/
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 0, 0);
|
||||||
|
hr = UiaGetRootNode(&node);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
ok(!!node, "Node == NULL.\n");
|
||||||
|
check_uia_hwnd_expects(1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
|
||||||
|
|
||||||
|
Provider_proxy.win_event_handler_data.is_supported = TRUE;
|
||||||
|
Provider_hwnd.win_event_handler_data.is_supported = Provider_nc.win_event_handler_data.is_supported = TRUE;
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_nc, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_hwnd, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF);
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_nc, &Provider_child.IRawElementProviderSimple_iface,
|
||||||
|
UIA_AutomationFocusChangedEventId);
|
||||||
|
|
||||||
|
/* Register a focus change event handler on the desktop HWND. */
|
||||||
|
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req,
|
||||||
|
&event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
test_provider_event_advise_added(&Provider_proxy, 0, TRUE);
|
||||||
|
test_provider_event_advise_added(&Provider_hwnd, 0, TRUE);
|
||||||
|
test_provider_event_advise_added(&Provider_nc, 0, TRUE);
|
||||||
|
|
||||||
|
/* Raise WinEvent on the desktop HWND. */
|
||||||
|
set_provider_runtime_id(&Provider_child, UIA_RUNTIME_ID_PREFIX, HandleToUlong(GetDesktopWindow()));
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_hwnd, &Provider_child.IRawElementProviderSimple_iface,
|
||||||
|
UIA_AutomationFocusChangedEventId);
|
||||||
|
set_uia_hwnd_expects(0, 1, 1, 0, 0);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Top-level HWND, a child of the desktop HWND. Will not have an event
|
||||||
|
* raised since it was not visible when the desktop providers were advised
|
||||||
|
* of an event being added.
|
||||||
|
*/
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
/* Test child hwnd, same deal. */
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Show window after calling UiaAddEvent(), does nothing. Window must be
|
||||||
|
* visible when provider is advised of an event being added.
|
||||||
|
*/
|
||||||
|
ShowWindow(hwnd[0], SW_SHOW);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the event again, except this time our test HWND was visible when
|
||||||
|
* the desktop provider was advised that our event was being added. Now
|
||||||
|
* WinEvents on our test HWND will be handled.
|
||||||
|
*/
|
||||||
|
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req,
|
||||||
|
&event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
test_provider_event_advise_added(&Provider_hwnd, 0, TRUE);
|
||||||
|
test_provider_event_advise_added(&Provider_nc, 0, TRUE);
|
||||||
|
test_provider_event_advise_added(&Provider_proxy, 0, TRUE);
|
||||||
|
|
||||||
|
/* WinEvent handled. */
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 2, 1);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 2, TRUE, 1, TRUE);
|
||||||
|
|
||||||
|
/* Child HWNDs of our test window are handled as well. */
|
||||||
|
SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2);
|
||||||
|
set_uia_hwnd_expects(0, 1, 1, 1, 0);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
UiaNodeRelease(node);
|
||||||
|
|
||||||
|
skip_win_event_hwnd_filter_test:
|
||||||
|
/*
|
||||||
|
* Test default MSAA proxy WinEvent handler.
|
||||||
|
*/
|
||||||
|
prov_root = &Provider.IRawElementProviderSimple_iface;
|
||||||
|
set_uia_hwnd_expects(2, 1, 1, 2, 0);
|
||||||
|
hr = UiaNodeFromHandle(hwnd[0], &node);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
ok(!!node, "Node == NULL.\n");
|
||||||
|
check_uia_hwnd_expects_at_most(1, 1, 1, 2, 0);
|
||||||
|
|
||||||
|
Provider_hwnd2.win_event_handler_data.is_supported = Provider_nc2.win_event_handler_data.is_supported = TRUE;
|
||||||
|
hr = UiaAddEvent(node, UIA_SystemAlertEventId, uia_event_callback, TreeScope_Subtree, NULL, 0, &cache_req,
|
||||||
|
&event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
UiaNodeRelease(node);
|
||||||
|
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_hwnd2, NULL, 0);
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_hwnd2, 0, hwnd[0], 0, 0);
|
||||||
|
set_provider_win_event_handler_respond_prov(&Provider_nc2, NULL, 0);
|
||||||
|
set_provider_win_event_handler_win_event_expects(&Provider_nc2, 0, NULL, 0, 0);
|
||||||
|
|
||||||
|
prov_root = NULL;
|
||||||
|
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
|
||||||
|
add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA proxy. */
|
||||||
|
set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)");
|
||||||
|
|
||||||
|
/* WinEvent handled by default MSAA proxy provider. */
|
||||||
|
set_uia_hwnd_expects(1, 1, 1, 4, 5);
|
||||||
|
test_uia_event_win_event_mapping(EVENT_SYSTEM_ALERT, hwnd[0], OBJID_CLIENT, 2, event_handles,
|
||||||
|
1, TRUE, FALSE, TRUE);
|
||||||
|
check_uia_hwnd_expects_at_most(1, 1, 1, 4, 5);
|
||||||
|
|
||||||
|
hr = UiaRemoveEvent(event);
|
||||||
|
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(event_handles); i++)
|
||||||
|
CloseHandle(event_handles[i]);
|
||||||
|
method_sequences_enabled = TRUE;
|
||||||
|
CoUninitialize();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_uia_event_ProxyProviderWinEventHandler(void)
|
||||||
|
{
|
||||||
|
HANDLE thread;
|
||||||
|
HWND hwnd[2];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows 7 behaves different than all other versions, just skip the
|
||||||
|
* tests.
|
||||||
|
*/
|
||||||
|
if (!UiaLookupId(AutomationIdentifierType_Property, &OptimizeForVisualContent_Property_GUID))
|
||||||
|
{
|
||||||
|
win_skip("Skipping IProxyProviderWinEventSink tests for Win7\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||||
|
|
||||||
|
hwnd[0] = create_test_hwnd("ProxyProviderWinEventHandler test class");
|
||||||
|
hwnd[1] = create_child_test_hwnd("ProxyProviderWinEventHandler test child class", hwnd[0]);
|
||||||
|
|
||||||
|
UiaRegisterProviderCallback(test_uia_provider_callback);
|
||||||
|
|
||||||
|
/* Set clientside providers for our test windows and the desktop. */
|
||||||
|
set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow());
|
||||||
|
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[0]);
|
||||||
|
initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd[0], TRUE);
|
||||||
|
Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface;
|
||||||
|
Provider.ignore_hwnd_prop = TRUE;
|
||||||
|
|
||||||
|
set_clientside_providers_for_hwnd(NULL, &Provider_nc3, &Provider_hwnd3, hwnd[1]);
|
||||||
|
initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, hwnd[1], TRUE);
|
||||||
|
Provider2.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface;
|
||||||
|
Provider2.ignore_hwnd_prop = TRUE;
|
||||||
|
|
||||||
|
prov_root = &Provider.IRawElementProviderSimple_iface;
|
||||||
|
child_win_prov_root = &Provider2.IRawElementProviderSimple_iface;
|
||||||
|
|
||||||
|
thread = CreateThread(NULL, 0, uia_proxy_provider_win_event_handler_test_thread, (void *)hwnd, 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);
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
destroy_test_hwnd(hwnd[0], "ProxyProviderWinEventHandler test class", "ProxyProviderWinEventHandler test child class");
|
||||||
|
UiaRegisterProviderCallback(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once a process returns a UI Automation provider with
|
* Once a process returns a UI Automation provider with
|
||||||
* UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This
|
* UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This
|
||||||
|
@ -16590,6 +17224,7 @@ START_TEST(uiautomation)
|
||||||
test_UiaNodeFromFocus();
|
test_UiaNodeFromFocus();
|
||||||
test_UiaAddEvent(argv[0]);
|
test_UiaAddEvent(argv[0]);
|
||||||
test_UiaHasServerSideProvider();
|
test_UiaHasServerSideProvider();
|
||||||
|
test_uia_event_ProxyProviderWinEventHandler();
|
||||||
if (uia_dll)
|
if (uia_dll)
|
||||||
{
|
{
|
||||||
pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
|
pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
|
||||||
|
|
Loading…
Add table
Reference in a new issue