uiautomationcore: Add support for translating EVENT_OBJECT_FOCUS for native MSAA IAccessibles.
Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
parent
f26d470920
commit
cc75dbd315
4 changed files with 248 additions and 4 deletions
|
@ -582,6 +582,18 @@ static struct Accessible
|
|||
(acc)->expect_ ## method = (acc)->called_ ## method = 0; \
|
||||
}while(0)
|
||||
|
||||
#define CHECK_ACC_METHOD_CALLED_AT_LEAST(acc, method, num) \
|
||||
do { \
|
||||
ok((acc)->called_ ## method >= num, "expected %s_" #method " at least %d time(s) (got %d)\n", (acc)->interface_name, num, (acc)->called_ ## method); \
|
||||
(acc)->expect_ ## method = (acc)->called_ ## method = 0; \
|
||||
}while(0)
|
||||
|
||||
#define CHECK_ACC_METHOD_CALLED_AT_MOST(acc, method, num) \
|
||||
do { \
|
||||
ok((acc)->called_ ## method <= num, "expected %s_" #method " at most %d time(s) (got %d)\n", (acc)->interface_name, num, (acc)->called_ ## method); \
|
||||
(acc)->expect_ ## method = (acc)->called_ ## method = 0; \
|
||||
}while(0)
|
||||
|
||||
static inline struct Accessible* impl_from_Accessible(IAccessible *iface)
|
||||
{
|
||||
return CONTAINING_RECORD(iface, struct Accessible, IAccessible_iface);
|
||||
|
@ -15774,6 +15786,163 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma
|
|||
CHECK_CALLED(uia_com_event_callback);
|
||||
CHECK_CALLED(uia_event_callback);
|
||||
|
||||
/*
|
||||
* Windows 7 has quirky behavior around MSAA proxy creation, skip tests.
|
||||
*/
|
||||
if (!UiaLookupId(AutomationIdentifierType_Property, &OptimizeForVisualContent_Property_GUID))
|
||||
{
|
||||
win_skip("Skipping focus MSAA proxy tests for Win7\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an MSAA proxy, raises event on that.
|
||||
*/
|
||||
prov_root = NULL;
|
||||
set_accessible_props(&Accessible, ROLE_SYSTEM_CLIENT, STATE_SYSTEM_FOCUSED, 0, L"acc_name", 0, 0, 0, 0);
|
||||
Accessible.ow_hwnd = test_hwnd;
|
||||
Accessible.focus_child_id = CHILDID_SELF;
|
||||
acc_client = &Accessible.IAccessible_iface;
|
||||
|
||||
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), test_hwnd);
|
||||
add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE);
|
||||
add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE);
|
||||
add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA Proxy provider. */
|
||||
|
||||
set_multi_event_data(&exp_node_desc);
|
||||
set_com_event_data(&exp_node_desc);
|
||||
|
||||
set_uia_hwnd_expects(0, 1, 1, 4, 4); /* Win11 sends 4 WM_GETOBJECT messages, normally only 2. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, QI_IAccIdentity, 3);
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, get_accParent, 3);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accFocus);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accState);
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n");
|
||||
if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n");
|
||||
check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible, QI_IAccIdentity);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible, get_accParent);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accFocus);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accState);
|
||||
CHECK_CALLED(uia_com_event_callback);
|
||||
CHECK_CALLED(uia_event_callback);
|
||||
|
||||
/*
|
||||
* Return Accessible_child2 from get_accFocus.
|
||||
*/
|
||||
set_accessible_props(&Accessible, ROLE_SYSTEM_CLIENT, STATE_SYSTEM_FOCUSED, 0, L"acc_name", 0, 0, 0, 0);
|
||||
Accessible.focus_child_id = 4; /* 4 gets us Accessible_child2. */
|
||||
set_accessible_props(&Accessible_child2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSED, 0, L"acc_child2", 0, 0, 0, 0);
|
||||
|
||||
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
|
||||
add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA Proxy provider. */
|
||||
|
||||
set_multi_event_data(&exp_node_desc);
|
||||
set_com_event_data(&exp_node_desc);
|
||||
set_uia_hwnd_expects(0, 0, 0, 2, 3); /* Win11 sends 2/3 WM_GETOBJECT messages, normally only 1/2. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, QI_IAccIdentity, 4); /* Only done 4 times on Win11, normally 3. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, get_accParent, 3); /* Only done 3 times on Win11, normally 2. */
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accFocus);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accChild);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accRole);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, QI_IAccIdentity);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accFocus);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accRole);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, accNavigate); /* Wine only, Windows doesn't pass this through the DA wrapper. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible_child2, get_accParent, 2);
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible_child2, get_accState, 2);
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n");
|
||||
if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n");
|
||||
check_uia_hwnd_expects_at_least(0, FALSE, 0, FALSE, 0, FALSE, 1, FALSE, 2, FALSE);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED_AT_LEAST(&Accessible, QI_IAccIdentity, 3);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED_AT_LEAST(&Accessible, get_accParent, 2);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accFocus);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accChild);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accRole);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible_child2, QI_IAccIdentity);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accFocus);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accRole);
|
||||
CHECK_ACC_METHOD_CALLED_MULTI(&Accessible_child2, get_accParent, 2);
|
||||
CHECK_ACC_METHOD_CALLED_MULTI(&Accessible_child2, get_accState, 2);
|
||||
CHECK_ACC_METHOD_CALLED_AT_MOST(&Accessible_child2, accNavigate, 1);
|
||||
CHECK_CALLED(uia_com_event_callback);
|
||||
CHECK_CALLED(uia_event_callback);
|
||||
|
||||
/*
|
||||
* accFocus returns Accessible_child2, however it has
|
||||
* STATE_SYSTEM_INVISIBLE set. Falls back to Accessible.
|
||||
*/
|
||||
set_accessible_props(&Accessible_child2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_FOCUSED, 0, L"acc_child2", 0, 0, 0, 0);
|
||||
set_accessible_props(&Accessible, ROLE_SYSTEM_CLIENT, STATE_SYSTEM_FOCUSED, 0, L"acc_name", 0, 0, 0, 0);
|
||||
|
||||
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), test_hwnd);
|
||||
add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE);
|
||||
add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE);
|
||||
add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA Proxy provider. */
|
||||
|
||||
set_multi_event_data(&exp_node_desc);
|
||||
set_com_event_data(&exp_node_desc);
|
||||
set_uia_hwnd_expects(0, 1, 1, 4, 4); /* Win11 sends 4 WM_GETOBJECT messages, normally only 2. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, QI_IAccIdentity, 4); /* Only done 4 times on Win11, normally 2. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, get_accParent, 3); /* Only done 3 times on Win11, normally 1. */
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accFocus);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accChild);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accState);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, accNavigate); /* Wine only, Windows doesn't pass this through the DA wrapper. */
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, QI_IAccIdentity);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accParent);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accState);
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n");
|
||||
if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n");
|
||||
check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED_AT_LEAST(&Accessible, QI_IAccIdentity, 2);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible, get_accParent);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accFocus);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accChild);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accState);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible_child2, QI_IAccIdentity);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accParent);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accState);
|
||||
CHECK_ACC_METHOD_CALLED_AT_MOST(&Accessible_child2, accNavigate, 1);
|
||||
CHECK_CALLED(uia_com_event_callback);
|
||||
CHECK_CALLED(uia_event_callback);
|
||||
|
||||
/*
|
||||
* Get Accessible_child2 by raising an event with its child ID directly.
|
||||
* It will have its get_accFocus method called.
|
||||
*/
|
||||
init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL);
|
||||
add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA Proxy provider. */
|
||||
|
||||
set_multi_event_data(&exp_node_desc);
|
||||
set_com_event_data(&exp_node_desc);
|
||||
set_uia_hwnd_expects(0, 0, 0, 2, 2); /* Win11 sends 2/2 WM_GETOBJECT messages, normally only 1/1. */
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible, QI_IAccIdentity, 2); /* Only done 2 times on Win11, normally 1. */
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accParent); /* Only done on Win11. */
|
||||
SET_ACC_METHOD_EXPECT(&Accessible, get_accChild);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, QI_IAccIdentity);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accFocus);
|
||||
SET_ACC_METHOD_EXPECT(&Accessible_child2, get_accState);
|
||||
SET_ACC_METHOD_EXPECT_MULTI(&Accessible_child2, get_accParent, 2);
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, 4);
|
||||
ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n");
|
||||
if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n");
|
||||
check_uia_hwnd_expects_at_least(0, FALSE, 0, FALSE, 0, FALSE, 1, FALSE, 1, FALSE);
|
||||
CHECK_ACC_METHOD_CALLED_AT_MOST(&Accessible, get_accParent, 1); /* Only done on Win11. */
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible, QI_IAccIdentity);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible, get_accChild);
|
||||
todo_wine CHECK_ACC_METHOD_CALLED(&Accessible_child2, QI_IAccIdentity);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accFocus);
|
||||
CHECK_ACC_METHOD_CALLED(&Accessible_child2, get_accState);
|
||||
CHECK_ACC_METHOD_CALLED_AT_MOST(&Accessible_child2, get_accParent, 2);
|
||||
CHECK_CALLED(uia_com_event_callback);
|
||||
CHECK_CALLED(uia_event_callback);
|
||||
acc_client = NULL;
|
||||
|
||||
exit:
|
||||
set_uia_hwnd_expects(0, 1, 1, 0, 0);
|
||||
hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface,
|
||||
&FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface);
|
||||
|
|
|
@ -1079,6 +1079,78 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia
|
|||
VariantClear(&v);
|
||||
}
|
||||
|
||||
static HRESULT uia_com_focus_win_event_msaa_callback(struct uia_event *event, void *user_data)
|
||||
{
|
||||
struct uia_event_args args = { { EventArgsType_Simple, UIA_AutomationFocusChangedEventId }, 0 };
|
||||
HUIANODE node = (HUIANODE)user_data;
|
||||
|
||||
/* Only match desktop events. */
|
||||
if (!event->desktop_subtree_event)
|
||||
return S_OK;
|
||||
|
||||
return uia_event_invoke(node, NULL, &args, event);
|
||||
}
|
||||
|
||||
static void uia_com_focus_win_event_msaa_handler(HWND hwnd, LONG child_id)
|
||||
{
|
||||
IRawElementProviderFragmentRoot *elroot;
|
||||
IRawElementProviderFragment *elfrag;
|
||||
IRawElementProviderSimple *elprov;
|
||||
HRESULT hr;
|
||||
VARIANT v;
|
||||
|
||||
hr = create_msaa_provider_from_hwnd(hwnd, child_id, &elprov);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("create_msaa_provider_from_hwnd failed with hr %#lx\n", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragmentRoot, (void **)&elroot);
|
||||
if (FAILED(hr))
|
||||
goto exit;
|
||||
|
||||
hr = IRawElementProviderFragmentRoot_GetFocus(elroot, &elfrag);
|
||||
IRawElementProviderFragmentRoot_Release(elroot);
|
||||
if (FAILED(hr))
|
||||
goto exit;
|
||||
|
||||
if (elfrag)
|
||||
{
|
||||
IRawElementProviderSimple *elprov2;
|
||||
|
||||
hr = IRawElementProviderFragment_QueryInterface(elfrag, &IID_IRawElementProviderSimple, (void **)&elprov2);
|
||||
IRawElementProviderFragment_Release(elfrag);
|
||||
if (FAILED(hr))
|
||||
goto exit;
|
||||
|
||||
IRawElementProviderSimple_Release(elprov);
|
||||
elprov = elprov2;
|
||||
}
|
||||
|
||||
VariantInit(&v);
|
||||
hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_HasKeyboardFocusPropertyId, &v);
|
||||
if (FAILED(hr))
|
||||
goto exit;
|
||||
|
||||
if (V_VT(&v) == VT_BOOL && V_BOOL(&v) == VARIANT_TRUE)
|
||||
{
|
||||
HUIANODE node;
|
||||
|
||||
hr = create_uia_node_from_elprov(elprov, &node, TRUE, 0);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = uia_event_for_each(UIA_AutomationFocusChangedEventId, uia_com_focus_win_event_msaa_callback, (void *)node, TRUE);
|
||||
if (FAILED(hr))
|
||||
WARN("uia_event_for_each failed with hr %#lx\n", hr);
|
||||
UiaNodeRelease(node);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
IRawElementProviderSimple_Release(elprov);
|
||||
}
|
||||
|
||||
HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, DWORD thread_id, DWORD event_time)
|
||||
{
|
||||
LONG handler_count;
|
||||
|
@ -1168,6 +1240,8 @@ HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG
|
|||
hr = create_uia_node_from_hwnd(hwnd, &node, NODE_FLAG_IGNORE_CLIENTSIDE_HWND_PROVS);
|
||||
if (SUCCEEDED(hr))
|
||||
uia_com_focus_win_event_handler(node, hwnd, event_id_map);
|
||||
else
|
||||
uia_com_focus_win_event_msaa_handler(hwnd, child_id);
|
||||
|
||||
UiaNodeRelease(node);
|
||||
}
|
||||
|
|
|
@ -587,8 +587,6 @@ static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue)
|
|||
return queue_event;
|
||||
}
|
||||
|
||||
static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args,
|
||||
struct uia_event *event);
|
||||
static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event)
|
||||
{
|
||||
HUIANODE node, nav_start_node;
|
||||
|
@ -676,7 +674,7 @@ static BOOL uia_win_event_hwnd_map_contains_ancestors(struct rb_tree *hwnd_map,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov)
|
||||
HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov)
|
||||
{
|
||||
IRawElementProviderSimple *elprov;
|
||||
IAccessible *acc;
|
||||
|
@ -1733,7 +1731,7 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, struct uia_event *event)
|
||||
HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, struct uia_event *event)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
|
|
|
@ -241,6 +241,7 @@ HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPE
|
|||
HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data,
|
||||
BOOL clientside_only) DECLSPEC_HIDDEN;
|
||||
BOOL uia_clientside_event_start_event_thread(struct uia_event *event) DECLSPEC_HIDDEN;
|
||||
HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov) DECLSPEC_HIDDEN;
|
||||
HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_id, LONG event_cookie) DECLSPEC_HIDDEN;
|
||||
HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events,
|
||||
struct uia_event *event) DECLSPEC_HIDDEN;
|
||||
|
@ -249,6 +250,8 @@ HRESULT uia_event_advise_node(struct uia_event *event, HUIANODE node) DECLSPEC_H
|
|||
HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids,
|
||||
int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback,
|
||||
void *cback_data, HUIAEVENT *huiaevent) DECLSPEC_HIDDEN;
|
||||
HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args,
|
||||
struct uia_event *event) DECLSPEC_HIDDEN;
|
||||
HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id,
|
||||
HUIANODE *clientside_nav_node_out) DECLSPEC_HIDDEN;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue