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

uiautomationcore: Add support for translating EVENT_OBJECT_FOCUS for native MSAA IAccessibles.

parent f26d4709
...@@ -582,6 +582,18 @@ static struct Accessible ...@@ -582,6 +582,18 @@ static struct Accessible
(acc)->expect_ ## method = (acc)->called_ ## method = 0; \ (acc)->expect_ ## method = (acc)->called_ ## method = 0; \
}while(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) static inline struct Accessible* impl_from_Accessible(IAccessible *iface)
{ {
return CONTAINING_RECORD(iface, struct 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 ...@@ -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_com_event_callback);
CHECK_CALLED(uia_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); set_uia_hwnd_expects(0, 1, 1, 0, 0);
hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface,
&FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface);
......
...@@ -1079,6 +1079,78 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia ...@@ -1079,6 +1079,78 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia
VariantClear(&v); 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) 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; LONG handler_count;
...@@ -1168,6 +1240,8 @@ HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG ...@@ -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); hr = create_uia_node_from_hwnd(hwnd, &node, NODE_FLAG_IGNORE_CLIENTSIDE_HWND_PROVS);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
uia_com_focus_win_event_handler(node, hwnd, event_id_map); uia_com_focus_win_event_handler(node, hwnd, event_id_map);
else
uia_com_focus_win_event_msaa_handler(hwnd, child_id);
UiaNodeRelease(node); UiaNodeRelease(node);
} }
......
...@@ -587,8 +587,6 @@ static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue) ...@@ -587,8 +587,6 @@ static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue)
return queue_event; 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) static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event)
{ {
HUIANODE node, nav_start_node; HUIANODE node, nav_start_node;
...@@ -676,7 +674,7 @@ static BOOL uia_win_event_hwnd_map_contains_ancestors(struct rb_tree *hwnd_map, ...@@ -676,7 +674,7 @@ static BOOL uia_win_event_hwnd_map_contains_ancestors(struct rb_tree *hwnd_map,
return FALSE; 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; IRawElementProviderSimple *elprov;
IAccessible *acc; IAccessible *acc;
...@@ -1733,7 +1731,7 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) ...@@ -1733,7 +1731,7 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
return S_OK; 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; HRESULT hr = S_OK;
......
...@@ -241,6 +241,7 @@ HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPE ...@@ -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, HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data,
BOOL clientside_only) DECLSPEC_HIDDEN; BOOL clientside_only) DECLSPEC_HIDDEN;
BOOL uia_clientside_event_start_event_thread(struct uia_event *event) 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 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, HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events,
struct uia_event *event) DECLSPEC_HIDDEN; struct uia_event *event) DECLSPEC_HIDDEN;
...@@ -249,6 +250,8 @@ HRESULT uia_event_advise_node(struct uia_event *event, HUIANODE node) DECLSPEC_H ...@@ -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, 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, int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback,
void *cback_data, HUIAEVENT *huiaevent) DECLSPEC_HIDDEN; 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, HRESULT uia_event_check_node_within_event_scope(struct uia_event *event, HUIANODE node, SAFEARRAY *rt_id,
HUIANODE *clientside_nav_node_out) DECLSPEC_HIDDEN; HUIANODE *clientside_nav_node_out) DECLSPEC_HIDDEN;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment