From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 78 ++++++++++++++++++++++ dlls/uiautomationcore/uia_classes.idl | 2 +- dlls/uiautomationcore/uia_client.c | 37 ++++++---- dlls/uiautomationcore/uia_com_client.c | 29 ++++++-- dlls/uiautomationcore/uia_event.c | 4 +- dlls/uiautomationcore/uia_private.h | 3 +- 6 files changed, 131 insertions(+), 22 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b6f8b3d75bb..300fc434505 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -14681,6 +14681,12 @@ static void set_com_event_data(struct node_provider_desc *exp_node_desc) SET_EXPECT(uia_com_event_callback); }
+static void push_expected_com_event(struct node_provider_desc *exp_node_desc) +{ + push_event_queue_event(&ComEventData.exp_events, exp_node_desc); + SET_EXPECT_MULTI(uia_com_event_callback, ComEventData.exp_events.exp_event_count); +} + #define test_com_event_data( sender ) \ test_com_event_data_( (sender), __FILE__, __LINE__) static void test_com_event_data_(IUIAutomationElement *sender, const char *file, int line) @@ -15560,6 +15566,12 @@ static void set_multi_event_data(struct node_provider_desc *exp_node_desc) SET_EXPECT(uia_event_callback); }
+static void push_expected_event(struct node_provider_desc *exp_node_desc) +{ + push_event_queue_event(&MultiEventData.exp_events, exp_node_desc); + SET_EXPECT_MULTI(uia_event_callback, MultiEventData.exp_events.exp_event_count); +} + static void WINAPI multi_uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) { struct node_provider_desc *exp_desc; @@ -15696,6 +15708,72 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma CHECK_CALLED(winproc_GETOBJECT_UiaRoot); set_provider_prop_override(&Provider, NULL, 0);
+ /* + * The first time EVENT_OBJECT_FOCUS is raised for an HWND with a + * serverside provider UIA will query for the currently focused provider + * and raise a focus change event for it, alongside advising the root + * provider of focus change events being listened for. All subsequent + * EVENT_OBJECT_FOCUS events on the same HWND only query the root + * provider. + */ + initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, TRUE); + Provider_child2.parent = &Provider2.IRawElementProviderFragment_iface; + Provider_child2.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface; + Provider2.focus_prov = &Provider_child2.IRawElementProviderFragment_iface; + set_provider_runtime_id(&Provider_child2, UiaAppendRuntimeId, 2); + initialize_provider_advise_events_ids(&Provider2); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child2", TRUE); + + set_multi_event_data(&exp_node_desc); + set_com_event_data(&exp_node_desc); + + /* Second event. */ + init_node_provider_desc(&exp_nested_node_desc, GetCurrentProcessId(), test_child_hwnd); + add_provider_desc(&exp_nested_node_desc, L"Main", L"Provider2", TRUE); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), test_child_hwnd); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd3", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc3", FALSE); + add_nested_provider_desc(&exp_node_desc, L"Main", NULL, FALSE, &exp_nested_node_desc); + + push_expected_event(&exp_node_desc); + push_expected_com_event(&exp_node_desc); + set_uia_hwnd_expects(0, 2, 2, 3, 0); /* Win11 sends 3 WM_GETOBJECT messages, normally only 2. */ + SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_child_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, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE); + CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 3); + CHECK_CALLED_MULTI(uia_com_event_callback, 2); + CHECK_CALLED_MULTI(uia_event_callback, 2); + + /* + * Second time EVENT_OBJECT_FOCUS is raised for this HWND, only the root + * provider will have an event raised. + */ + init_node_provider_desc(&exp_nested_node_desc, GetCurrentProcessId(), test_child_hwnd); + add_provider_desc(&exp_nested_node_desc, L"Main", L"Provider2", TRUE); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), test_child_hwnd); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd3", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc3", FALSE); + add_nested_provider_desc(&exp_node_desc, L"Main", NULL, FALSE, &exp_nested_node_desc); + + set_multi_event_data(&exp_node_desc); + set_com_event_data(&exp_node_desc); + set_uia_hwnd_expects(0, 2, 2, 3, 0); /* Win11 sends 3 WM_GETOBJECT messages, normally only 2. */ + SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_child_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, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE); + CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 3); + CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_event_callback); + set_uia_hwnd_expects(0, 1, 1, 0, 0); hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index ed1a1aa3f04..7b3de05268f 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -91,7 +91,7 @@ library UIA_wine_private HRESULT get_prov_opts([out, retval]int *out_opts); HRESULT has_parent([out, retval]BOOL *out_val); HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val); - HRESULT get_focus([out, retval]VARIANT *ret_val); + HRESULT get_focus([in]long flags, [out, retval]VARIANT *ret_val); HRESULT attach_event([in]LONG_PTR huiaevent); HRESULT respond_to_win_event([in]DWORD win_event, [in]ULONG hwnd, [in]LONG obj_id, [in]LONG child_id, [in]IProxyProviderWinEventSink *sink); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index a7ea3998ff6..22cce1324e8 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -347,7 +347,7 @@ static HRESULT get_navigate_from_node_provider(IWineUiaNode *node, int idx, int return hr; }
-static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT *ret_val) +HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, LONG flags, VARIANT *ret_val) { IWineUiaProvider *prov; HRESULT hr; @@ -357,7 +357,7 @@ static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT if (FAILED(hr)) return hr;
- hr = IWineUiaProvider_get_focus(prov, ret_val); + hr = IWineUiaProvider_get_focus(prov, flags, ret_val); IWineUiaProvider_Release(prov);
return hr; @@ -1836,7 +1836,7 @@ static HRESULT WINAPI uia_provider_navigate(IWineUiaProvider *iface, int nav_dir return S_OK; }
-static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *out_val) +static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, LONG flags, VARIANT *out_val) { struct uia_provider *prov = impl_from_IWineUiaProvider(iface); IRawElementProviderFragmentRoot *elroot; @@ -1844,7 +1844,10 @@ static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *o IRawElementProviderSimple *elprov; HRESULT hr;
- TRACE("%p, %p\n", iface, out_val); + TRACE("%p, %#lx, %p\n", iface, flags, out_val); + + if (flags & PROV_METHOD_FLAG_RETURN_NODE_LRES) + FIXME("PROV_METHOD_FLAG_RETURN_NODE_LRES ignored for normal providers.\n");
VariantInit(out_val); hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragmentRoot, (void **)&elroot); @@ -2357,7 +2360,7 @@ static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *if { HUIANODE node;
- hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node, 0); if (FAILED(hr)) return hr;
@@ -2406,7 +2409,7 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
- hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node, 0); if (FAILED(hr)) return hr;
@@ -2416,21 +2419,27 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, return S_OK; }
-static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface, VARIANT *out_val) +static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface, LONG flags, VARIANT *out_val) { struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); HUIANODE node; HRESULT hr; VARIANT v;
- TRACE("%p, %p\n", iface, out_val); + TRACE("%p, %#lx, %p\n", iface, flags, out_val);
VariantInit(out_val); - hr = get_focus_from_node_provider(prov->nested_node, 0, &v); + hr = get_focus_from_node_provider(prov->nested_node, 0, 0, &v); if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
- hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + if (flags & PROV_METHOD_FLAG_RETURN_NODE_LRES) + { + *out_val = v; + return S_OK; + } + + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node, 0); if (FAILED(hr)) return hr;
@@ -2488,7 +2497,7 @@ static HRESULT WINAPI uia_nested_node_provider_create_node_from_prov(IWineUiaPro return S_OK; }
- hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node, 0); if (FAILED(hr)) return hr;
@@ -2631,14 +2640,14 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU return S_OK; }
-HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) +HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode, int node_flags) { struct uia_node *node; HRESULT hr;
*huianode = NULL;
- hr = create_uia_node(&node, 0); + hr = create_uia_node(&node, node_flags); if (FAILED(hr)) { uia_node_lresult_release(lr); @@ -2804,7 +2813,7 @@ static HRESULT get_focused_uia_node(HUIANODE in_node, HUIANODE *out_node) (get_node_provider_type_at_idx(node, i) == PROV_TYPE_HWND))) continue;
- hr = get_focus_from_node_provider(&node->IWineUiaNode_iface, i, &v); + hr = get_focus_from_node_provider(&node->IWineUiaNode_iface, i, 0, &v); if (FAILED(hr)) break;
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index e2fd8f5b5b5..d78b5c5366d 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1013,7 +1013,7 @@ static HRESULT uia_com_focus_win_event_callback(struct uia_event *event, void *u return hr; }
-static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIANODE node, HWND hwnd) +static BOOL uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIANODE node, HWND hwnd) { HRESULT hr;
@@ -1021,12 +1021,15 @@ static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIAN if (FAILED(hr)) { WARN("uia_event_advise_node failed with hr %#lx\n", hr); - return; + goto exit; }
hr = uia_hwnd_map_add_hwnd(&event->focus_hwnd_map, hwnd); if (FAILED(hr)) WARN("Failed to add hwnd for focus winevent, hr %#lx\n", hr); + +exit: + return SUCCEEDED(hr); }
static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia_event_handler_event_id_map_entry *event_id_map) @@ -1041,8 +1044,26 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia
LIST_FOR_EACH_ENTRY(event, &entry->handlers_list, struct uia_com_event, event_handler_map_list_entry) { - if (!uia_hwnd_map_check_hwnd(&event->focus_hwnd_map, hwnd)) - uia_com_focus_handler_advise_node(event, node, hwnd); + if (uia_hwnd_map_check_hwnd(&event->focus_hwnd_map, hwnd) || + !uia_com_focus_handler_advise_node(event, node, hwnd)) + continue; + + hr = get_focus_from_node_provider((IWineUiaNode *)node, 0, PROV_METHOD_FLAG_RETURN_NODE_LRES, &v); + if (V_VT(&v) == VT_I4) + { + HUIANODE focus_node = NULL; + + hr = uia_node_from_lresult(V_I4(&v), &focus_node, NODE_FLAG_IGNORE_CLIENTSIDE_HWND_PROVS); + if (SUCCEEDED(hr)) + { + hr = uia_event_for_each(UIA_AutomationFocusChangedEventId, uia_com_focus_win_event_callback, + (void *)focus_node, TRUE); + if (FAILED(hr)) + WARN("uia_event_for_each on focus_node failed with hr %#lx\n", hr); + } + UiaNodeRelease(focus_node); + } + VariantClear(&v); } }
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index e85bcf2e825..15b3b84d96b 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -595,7 +595,7 @@ static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event) HRESULT hr;
node = nav_start_node = NULL; - hr = uia_node_from_lresult(event->u.clientside.node, &node); + hr = uia_node_from_lresult(event->u.clientside.node, &node, 0); if (FAILED(hr)) { WARN("Failed to create node from lresult, hr %#lx\n", hr); @@ -605,7 +605,7 @@ static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event)
if (event->u.clientside.nav_start_node) { - hr = uia_node_from_lresult(event->u.clientside.nav_start_node, &nav_start_node); + hr = uia_node_from_lresult(event->u.clientside.nav_start_node, &nav_start_node, 0); if (FAILED(hr)) { WARN("Failed to create nav_start_node from lresult, hr %#lx\n", hr); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index b7b379cdb87..00ed1f8f293 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -216,6 +216,7 @@ enum provider_method_flags {
/* uia_client.c */ int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; +HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, LONG flags, VARIANT *ret_val) DECLSPEC_HIDDEN; HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, LONG child_id, IProxyProviderWinEventSink *sink) DECLSPEC_HIDDEN; HRESULT create_node_from_node_provider(IWineUiaNode *node, int idx, LONG flags, VARIANT *ret_val) DECLSPEC_HIDDEN; @@ -224,7 +225,7 @@ HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers, int node_flags) DECLSPEC_HIDDEN; -HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) DECLSPEC_HIDDEN; +HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode, int node_flags) DECLSPEC_HIDDEN; void uia_node_lresult_release(LRESULT lr) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_hwnd(HWND hwnd, HUIANODE *out_node, int node_flags) DECLSPEC_HIDDEN; HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) DECLSPEC_HIDDEN;