-- v2: uiautomationcore: Add support for translating EVENT_OBJECT_FOCUS for native MSAA IAccessibles. uiautomationcore: Potentially raise focus event on the currently focused serverside provider in response to EVENT_OBJECT_FOCUS. uiautomationcore: Potentially raise focus event for serverside providers in response to EVENT_OBJECT_FOCUS. uiautomationcore/tests: Add tests for COM API EVENT_OBJECT_FOCUS translation. uiautomationcore: Release node lresult upon failure to allocate a node in uia_node_from_lresult().
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 11 +++++++++++ dlls/uiautomationcore/uia_event.c | 8 -------- dlls/uiautomationcore/uia_private.h | 1 + 3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index f57ffb4bc4c..88e607b6e6a 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2572,7 +2572,10 @@ HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode)
hr = create_uia_node(&node, 0); if (FAILED(hr)) + { + uia_node_lresult_release(lr); return hr; + }
uia_start_client_thread(); hr = create_wine_uia_nested_node_provider(node, lr, FALSE); @@ -2601,6 +2604,14 @@ HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) return hr; }
+void uia_node_lresult_release(LRESULT lr) +{ + IWineUiaNode *node; + + if (lr && SUCCEEDED(ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&node))) + IWineUiaNode_Release(node); +} + /* * UiaNodeFromHandle is expected to work even if the calling thread hasn't * initialized COM. We marshal our node on a separate thread that initializes diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 2cb61a2062e..a82af6b495c 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -588,14 +588,6 @@ static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue) return queue_event; }
-static void uia_node_lresult_release(LRESULT lr) -{ - IWineUiaNode *node; - - if (lr && SUCCEEDED(ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&node))) - IWineUiaNode_Release(node); -} - 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) diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index b55379b459d..49d42cbeb45 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -219,6 +219,7 @@ HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node 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; +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; BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 228 +++++++++++++++++++-- 1 file changed, 215 insertions(+), 13 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 7790907d674..b4c920bac71 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -14619,33 +14619,64 @@ static void check_uia_hwnd_expects_at_least(int proxy_cback_count, BOOL proxy_cb todo_wine_if(win_get_client_obj_todo) CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count); }
-static struct ComEventData { - struct node_provider_desc exp_node_desc; - struct node_provider_desc exp_nested_node_desc; - - HWND event_hwnd; - DWORD last_call_tid; -} ComEventData; +#define MAX_EVENT_QUEUE_COUNT 4 +struct ExpectedEventQueue { + struct node_provider_desc exp_node_desc[MAX_EVENT_QUEUE_COUNT]; + struct node_provider_desc exp_nested_node_desc[MAX_EVENT_QUEUE_COUNT]; + int exp_event_count; + int exp_event_pos; +};
-static void set_com_event_data(struct node_provider_desc *exp_node_desc) +static void push_event_queue_event(struct ExpectedEventQueue *queue, struct node_provider_desc *exp_node_desc) { + const int idx = queue->exp_event_count; + + assert(idx < MAX_EVENT_QUEUE_COUNT); + if (exp_node_desc) { int i;
- ComEventData.exp_node_desc = *exp_node_desc; + queue->exp_node_desc[idx] = *exp_node_desc; for (i = 0; i < exp_node_desc->prov_count; i++) { if (exp_node_desc->nested_desc[i]) { - ComEventData.exp_nested_node_desc = *exp_node_desc->nested_desc[i]; - ComEventData.exp_node_desc.nested_desc[i] = &ComEventData.exp_nested_node_desc; + queue->exp_nested_node_desc[idx] = *exp_node_desc->nested_desc[i]; + queue->exp_node_desc[idx].nested_desc[i] = &queue->exp_nested_node_desc[idx]; break; } } } else - memset(&ComEventData.exp_node_desc, 0, sizeof(ComEventData.exp_node_desc)); + memset(&queue->exp_node_desc[idx], 0, sizeof(queue->exp_node_desc[idx])); + queue->exp_event_count++; +} + +static struct node_provider_desc *pop_event_queue_event(struct ExpectedEventQueue *queue) +{ + if (!queue->exp_event_count || queue->exp_event_pos >= queue->exp_event_count) + { + ok(0, "Failed to pop expected event from queue\n"); + return NULL; + } + + return &queue->exp_node_desc[queue->exp_event_pos++]; +} + +static struct ComEventData { + struct ExpectedEventQueue exp_events; + + HWND event_hwnd; + DWORD last_call_tid; + HANDLE event_handle; +} ComEventData; + +static void set_com_event_data(struct node_provider_desc *exp_node_desc) +{ + memset(&ComEventData.exp_events, 0, sizeof(ComEventData.exp_events)); + push_event_queue_event(&ComEventData.exp_events, exp_node_desc); + ComEventData.last_call_tid = 0; SET_EXPECT(uia_com_event_callback); } @@ -14654,6 +14685,7 @@ static void set_com_event_data(struct node_provider_desc *exp_node_desc) test_com_event_data_( (sender), __FILE__, __LINE__) static void test_com_event_data_(IUIAutomationElement *sender, const char *file, int line) { + struct node_provider_desc *exp_desc = pop_event_queue_event(&ComEventData.exp_events); HRESULT hr; VARIANT v;
@@ -14662,10 +14694,12 @@ static void test_com_event_data_(IUIAutomationElement *sender, const char *file, VariantInit(&v); hr = IUIAutomationElement_GetCurrentPropertyValueEx(sender, UIA_ProviderDescriptionPropertyId, TRUE, &v); ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); - test_node_provider_desc_(&ComEventData.exp_node_desc, V_BSTR(&v), file, line); + test_node_provider_desc_(exp_desc, V_BSTR(&v), file, line); VariantClear(&v);
ComEventData.last_call_tid = GetCurrentThreadId(); + if (ComEventData.event_handle && (ComEventData.exp_events.exp_event_count == ComEventData.exp_events.exp_event_pos)) + SetEvent(ComEventData.event_handle); }
/* @@ -15506,10 +15540,176 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
exit: + Provider_hwnd3.parent = &Provider_hwnd2.IRawElementProviderFragment_iface; for (i = 0; i < ARRAY_SIZE(method_event); i++) CloseHandle(method_event[i]); }
+static struct MultiEventData { + struct ExpectedEventQueue exp_events; + + HANDLE event_handle; + BOOL ignore_callback; +} MultiEventData; + +static void set_multi_event_data(struct node_provider_desc *exp_node_desc) +{ + memset(&MultiEventData.exp_events, 0, sizeof(MultiEventData.exp_events)); + push_event_queue_event(&MultiEventData.exp_events, exp_node_desc); + + SET_EXPECT(uia_event_callback); +} + +static void WINAPI multi_uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) +{ + struct node_provider_desc *exp_desc; + LONG exp_lbound[2], exp_elems[2]; + + if (MultiEventData.ignore_callback) + return; + + CHECK_EXPECT(uia_event_callback); + + exp_lbound[0] = exp_lbound[1] = 0; + exp_elems[0] = exp_elems[1] = 1; + exp_desc = pop_event_queue_event(&MultiEventData.exp_events); + test_cache_req_sa(req_data, exp_lbound, exp_elems, exp_desc); + + ok(!wcscmp(tree_struct, L"P)"), "tree structure %s\n", debugstr_w(tree_struct)); + if (MultiEventData.event_handle && (MultiEventData.exp_events.exp_event_count == MultiEventData.exp_events.exp_event_pos)) + SetEvent(MultiEventData.event_handle); +} + +static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutomation *uia_iface, HWND test_hwnd, + HWND test_child_hwnd) +{ + struct UiaCacheRequest cache_req = { (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full }; + struct node_provider_desc exp_node_desc, exp_nested_node_desc; + struct Provider_prop_override prop_override; + HANDLE event_handles[2]; + HUIAEVENT event; + HUIANODE node; + HRESULT hr; + VARIANT v; + int i; + + for (i = 0; i < ARRAY_SIZE(event_handles); i++) + event_handles[i] = CreateEventW(NULL, FALSE, FALSE, NULL); + + set_uia_hwnd_expects(0, 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(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE); + + set_uia_hwnd_expects(0, 6, 6, 5, 0); + SET_EXPECT(child_winproc_GETOBJECT_UiaRoot); /* Only sent on Win11. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, multi_uia_event_callback, TreeScope_Subtree, NULL, 0, &cache_req, + &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL.\n"); + check_uia_hwnd_expects_at_most(0, 6, 6, 5, 0); + CHECK_CALLED_AT_MOST(child_winproc_GETOBJECT_UiaRoot, 1); + + UiaNodeRelease(node); + + MultiEventData.event_handle = event_handles[0]; + ComEventData.event_handle = event_handles[1]; + + /* + * No IUIAutomationFocusChangedEventHandler is installed, and no + * IProxyProviderWinEventHandler interfaces were returned from our + * clientside desktop providers. This WinEvent will be ignored. + */ + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF); + ok(msg_wait_for_all_events(event_handles, 1, 750) == WAIT_TIMEOUT, "Wait for event_handle(s) didn't time out.\n"); + + /* Add our IUIAutomationFocusChangedEventHandler. */ + set_uia_hwnd_expects(0, 6, 6, 5, 0); + SET_EXPECT(child_winproc_GETOBJECT_UiaRoot); /* Only sent on Win11. */ + MultiEventData.ignore_callback = TRUE; + FocusChangedHandler.event_handler_added = FALSE; + hr = IUIAutomation_AddFocusChangedEventHandler(uia_iface, NULL, + &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + check_uia_hwnd_expects_at_most(0, 6, 6, 5, 0); + CHECK_CALLED_AT_MOST(child_winproc_GETOBJECT_UiaRoot, 1); + + /* + * Now that we have an IUIAutomationFocusChangedEventHandler installed, + * EVENT_OBJECT_FOCUS events will be translated into native UIA events on + * our serverside provider. This is done for both HUIAEVENTs and COM + * events, unlike event advisement which only applies to COM event + * handlers. + */ + MultiEventData.ignore_callback = FALSE; + FocusChangedHandler.event_handler_added = TRUE; + init_node_provider_desc(&exp_nested_node_desc, GetCurrentProcessId(), test_hwnd); + add_provider_desc(&exp_nested_node_desc, L"Main", L"Provider", TRUE); + + 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_nested_provider_desc(&exp_node_desc, L"Main", NULL, TRUE, &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, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF); + todo_wine 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, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); + todo_wine CHECK_CALLED(uia_com_event_callback); + todo_wine CHECK_CALLED(uia_event_callback); + + /* + * Child ID is ignored when translating EVENT_OBJECT_FOCUS events into + * native UIA events on our serverside provider. + */ + set_multi_event_data(&exp_node_desc); + set_com_event_data(&exp_node_desc); + set_uia_hwnd_expects(0, 2, 2, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ + + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, -1); + todo_wine 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, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); + todo_wine CHECK_CALLED(uia_com_event_callback); + todo_wine CHECK_CALLED(uia_event_callback); + + /* + * UIA queries the serverside provider for UIA_HasKeyboardFocusPropertyId. + * If this is anything other than TRUE, it won't raise an event for our + * serverside provider. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_HasKeyboardFocusPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + + SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 2); /* Only done twice on Win11. */ + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF); + ok(msg_wait_for_all_events(event_handles, 2, 750) == WAIT_TIMEOUT, "Wait for event_handle(s) didn't time out.\n"); + if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + set_provider_prop_override(&Provider, NULL, 0); + + set_uia_hwnd_expects(0, 1, 1, 0, 0); + hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, + &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + + ComEventData.event_handle = MultiEventData.event_handle = NULL; + for (i = 0; i < ARRAY_SIZE(event_handles); i++) + CloseHandle(event_handles[i]); +} + static DWORD WINAPI uia_com_event_handler_win_event_test_thread(LPVOID param) { struct com_win_event_test_thread_data *test_data = (struct com_win_event_test_thread_data *)param; @@ -15519,7 +15719,9 @@ static DWORD WINAPI uia_com_event_handler_win_event_test_thread(LPVOID param) UiaRegisterProviderCallback(uia_com_win_event_clientside_provider_callback);
test_uia_com_event_handler_event_advisement(uia_iface, test_data->test_hwnd, test_data->test_child_hwnd); + test_uia_com_focus_change_event_handler_win_event_handling(uia_iface, test_data->test_hwnd, test_data->test_child_hwnd);
+ if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); UiaRegisterProviderCallback(NULL); CoUninitialize();
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 16 ++--- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 68 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 47 +++++++++++++++ dlls/uiautomationcore/uia_event.c | 3 +- dlls/uiautomationcore/uia_private.h | 8 +++ 6 files changed, 133 insertions(+), 10 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b4c920bac71..b6f8b3d75bb 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -15659,11 +15659,11 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma
set_uia_hwnd_expects(0, 2, 2, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */ NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, CHILDID_SELF); - todo_wine ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); + 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, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(uia_com_event_callback); - todo_wine CHECK_CALLED(uia_event_callback); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 3, FALSE, 0, FALSE); + CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_event_callback);
/* * Child ID is ignored when translating EVENT_OBJECT_FOCUS events into @@ -15674,11 +15674,11 @@ static void test_uia_com_focus_change_event_handler_win_event_handling(IUIAutoma set_uia_hwnd_expects(0, 2, 2, 4, 0); /* Win11 sends 4 WM_GETOBJECT messages, normally only 3. */
NotifyWinEvent(EVENT_OBJECT_FOCUS, test_hwnd, OBJID_CLIENT, -1); - todo_wine ok(msg_wait_for_all_events(event_handles, 2, 5000) != WAIT_TIMEOUT, "Wait for event_handle(s) timed out.\n"); + 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, TRUE, 2, TRUE, 3, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(uia_com_event_callback); - todo_wine CHECK_CALLED(uia_event_callback); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 3, FALSE, 0, FALSE); + CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_event_callback);
/* * UIA queries the serverside provider for UIA_HasKeyboardFocusPropertyId. diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 22d9d650502..ed1a1aa3f04 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -95,6 +95,7 @@ library UIA_wine_private 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); + HRESULT create_node_from_prov([in]long flags, [out, retval]VARIANT *ret_val); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 88e607b6e6a..a7ea3998ff6 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -394,6 +394,22 @@ HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD return hr; }
+HRESULT create_node_from_node_provider(IWineUiaNode *node, int idx, LONG flags, VARIANT *ret_val) +{ + IWineUiaProvider *prov; + HRESULT hr; + + VariantInit(ret_val); + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_create_node_from_prov(prov, flags, ret_val); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1972,6 +1988,26 @@ static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, return hr; }
+static HRESULT WINAPI uia_provider_create_node_from_prov(IWineUiaProvider *iface, LONG flags, VARIANT *ret_val) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IRawElementProviderSimple *elprov; + HRESULT hr; + + TRACE("%p, %#lx, %p\n", iface, flags, ret_val); + + if (flags & PROV_METHOD_FLAG_RETURN_NODE_LRES) + FIXME("PROV_METHOD_FLAG_RETURN_NODE_LRES ignored for normal providers.\n"); + + VariantInit(ret_val); + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderSimple, (void **)&elprov); + if (FAILED(hr)) + return hr; + + /* get_variant_for_elprov_node will release our provider upon failure. */ + return get_variant_for_elprov_node(elprov, prov->return_nested_node, prov->refuse_hwnd_node_providers, ret_val); +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1983,6 +2019,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_get_focus, uia_provider_attach_event, uia_provider_respond_to_win_event, + uia_provider_create_node_from_prov, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2431,6 +2468,36 @@ static HRESULT WINAPI uia_nested_node_provider_respond_to_win_event(IWineUiaProv return E_FAIL; }
+static HRESULT WINAPI uia_nested_node_provider_create_node_from_prov(IWineUiaProvider *iface, LONG flags, VARIANT *ret_val) +{ + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + HUIANODE node; + HRESULT hr; + VARIANT v; + + TRACE("%p, %#lx, %p\n", iface, flags, ret_val); + + VariantInit(ret_val); + hr = create_node_from_node_provider(prov->nested_node, 0, 0, &v); + if (FAILED(hr) || V_VT(&v) == VT_EMPTY) + return hr; + + if (flags & PROV_METHOD_FLAG_RETURN_NODE_LRES) + { + *ret_val = v; + return S_OK; + } + + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + if (FAILED(hr)) + return hr; + + get_variant_for_node(node, ret_val); + VariantClear(&v); + + return S_OK; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2442,6 +2509,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_get_focus, uia_nested_node_provider_attach_event, uia_nested_node_provider_respond_to_win_event, + uia_nested_node_provider_create_node_from_prov, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 91013dfc4ed..e2fd8f5b5b5 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -979,6 +979,40 @@ struct uia_com_event { struct uia_event_handler_map_entry *handler_map; };
+static HRESULT uia_com_focus_win_event_callback(struct uia_event *event, void *user_data) +{ + struct uia_node *node = impl_from_IWineUiaNode((IWineUiaNode *)user_data); + VARIANT v, v2; + HRESULT hr; + + /* Only match desktop events. */ + if (!event->desktop_subtree_event) + return S_OK; + + VariantInit(&v); + VariantInit(&v2); + + hr = create_node_from_node_provider(&node->IWineUiaNode_iface, 0, PROV_METHOD_FLAG_RETURN_NODE_LRES, &v); + if (FAILED(hr)) + { + WARN("Failed to create new node lres with hr %#lx\n", hr); + return hr; + } + + if (V_VT(&v) == VT_I4) + { + hr = IWineUiaEvent_raise_event(&event->IWineUiaEvent_iface, v, v2); + if (FAILED(hr)) + { + WARN("raise_event failed with hr %#lx\n", hr); + uia_node_lresult_release(V_I4(&v)); + } + } + VariantClear(&v); + + return hr; +} + static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIANODE node, HWND hwnd) { HRESULT hr; @@ -998,6 +1032,8 @@ static void uia_com_focus_handler_advise_node(struct uia_com_event *event, HUIAN static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia_event_handler_event_id_map_entry *event_id_map) { struct uia_event_handler_map_entry *entry; + HRESULT hr; + VARIANT v;
LIST_FOR_EACH_ENTRY(entry, &event_id_map->handlers_list, struct uia_event_handler_map_entry, handler_event_id_map_list_entry) { @@ -1009,6 +1045,17 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia uia_com_focus_handler_advise_node(event, node, hwnd); } } + + VariantInit(&v); + hr = UiaGetPropertyValue(node, UIA_HasKeyboardFocusPropertyId, &v); + if (SUCCEEDED(hr) && (V_VT(&v) == VT_BOOL && V_BOOL(&v) == VARIANT_TRUE)) + { + hr = uia_event_for_each(UIA_AutomationFocusChangedEventId, uia_com_focus_win_event_callback, (void *)node, TRUE); + if (FAILED(hr)) + WARN("uia_event_for_each failed with hr %#lx\n", hr); + } + + VariantClear(&v); }
HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, DWORD thread_id, DWORD event_time) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index a82af6b495c..e85bcf2e825 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -252,8 +252,7 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) } }
-typedef HRESULT UiaWineEventForEachCallback(struct uia_event *, void *); -static 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) { struct uia_event_map_entry *event_entry; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 49d42cbeb45..b7b379cdb87 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -158,6 +158,7 @@ struct uia_event };
typedef HRESULT UiaWineEventCallback(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); +typedef HRESULT UiaWineEventForEachCallback(struct uia_event *, void *);
static inline void variant_init_bool(VARIANT *v, BOOL val) { @@ -209,10 +210,15 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c return TRUE; }
+enum provider_method_flags { + PROV_METHOD_FLAG_RETURN_NODE_LRES = 0x0001, +}; + /* uia_client.c */ int get_node_provider_type_at_idx(struct uia_node *node, int idx) 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; HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; 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; @@ -231,6 +237,8 @@ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPEC_HIDDEN; +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_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,
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;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 169 +++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 74 +++++++++ dlls/uiautomationcore/uia_event.c | 6 +- dlls/uiautomationcore/uia_private.h | 3 + 4 files changed, 248 insertions(+), 4 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 300fc434505..f34f020b814 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -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); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index d78b5c5366d..d138d4ed919 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -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); } diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 15b3b84d96b..0106be09999 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -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;
diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 00ed1f8f293..a32d0c4d884 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -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;
On Thu Oct 12 12:48:30 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/4077/diffs?diff_id=75708&start_sha=a6598f00727cf47645d89020b45463bb0db36d2e#757856e6ad8b0244e53744faeb8594b6ec93b002_14676_14676)
Renamed in current revision.
On Thu Oct 12 12:48:30 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/4077/diffs?diff_id=75708&start_sha=a6598f00727cf47645d89020b45463bb0db36d2e#757856e6ad8b0244e53744faeb8594b6ec93b002_14670_14670)
Yep, good catch. Should be fixed now.
On Thu Oct 12 12:48:31 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/4077/diffs?diff_id=75708&start_sha=a6598f00727cf47645d89020b45463bb0db36d2e#58d90e31c9d28aee1bd4c53093a1f039ee491f9d_2547_2532)
Yeah the bracketed calls felt ugly to me as well, I've removed that and passed through a flags argument to the methods instead in the current revision now.
This merge request was approved by Esme Povirk.