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..f94075a4c81 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_STACK_COUNT 4 +struct ExpectedEventStack { + struct node_provider_desc exp_node_desc[MAX_EVENT_STACK_COUNT]; + struct node_provider_desc exp_nested_node_desc[MAX_EVENT_STACK_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_stack_event(struct ExpectedEventStack *stack, struct node_provider_desc *exp_node_desc) { + const int idx = stack->exp_event_count; + + assert(idx < MAX_EVENT_STACK_COUNT); + if (exp_node_desc) { int i;
- ComEventData.exp_node_desc = *exp_node_desc; + stack->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; + stack->exp_nested_node_desc[idx] = *exp_node_desc->nested_desc[i]; + stack->exp_node_desc[idx].nested_desc[i] = &stack->exp_nested_node_desc[idx]; break; } } } else - memset(&ComEventData.exp_node_desc, 0, sizeof(ComEventData.exp_node_desc)); + memset(&stack->exp_node_desc[idx], 0, sizeof(stack->exp_node_desc[idx])); + stack->exp_event_count++; +} + +static struct node_provider_desc *pop_event_stack_event(struct ExpectedEventStack *stack) +{ + if (!stack->exp_event_count || stack->exp_event_pos >= MAX_EVENT_STACK_COUNT) + { + ok(0, "Failed to pop expected event from stack\n"); + return NULL; + } + + return &stack->exp_node_desc[stack->exp_event_pos++]; +} + +static struct ComEventData { + struct ExpectedEventStack 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_stack_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_stack_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 ExpectedEventStack 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_stack_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_stack_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 | 94 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 49 +++++++++++ dlls/uiautomationcore/uia_event.c | 3 +- dlls/uiautomationcore/uia_private.h | 5 ++ 6 files changed, 158 insertions(+), 10 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f94075a4c81..d00222ad1e1 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..a24ce392063 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([out, retval]VARIANT *ret_val); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 88e607b6e6a..5e9c51f550f 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, 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, ret_val); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1972,6 +1988,23 @@ static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, return hr; }
+static HRESULT WINAPI uia_provider_create_node_from_prov(IWineUiaProvider *iface, VARIANT *ret_val) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IRawElementProviderSimple *elprov; + HRESULT hr; + + TRACE("%p, %p\n", iface, ret_val); + + 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 +2016,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, @@ -2250,6 +2284,7 @@ struct uia_nested_node_provider { LONG ref;
IWineUiaNode *nested_node; + BOOL return_node_lres; };
static inline struct uia_nested_node_provider *impl_from_nested_node_IWineUiaProvider(IWineUiaProvider *iface) @@ -2320,6 +2355,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *if { HUIANODE node;
+ if (prov->return_node_lres) + { + *ret_val = v; + break; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2369,6 +2410,12 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
+ if (prov->return_node_lres) + { + *out_val = v; + return S_OK; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2393,6 +2440,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface if (FAILED(hr) || V_VT(&v) == VT_EMPTY) return hr;
+ if (prov->return_node_lres) + { + *out_val = v; + return S_OK; + } + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); if (FAILED(hr)) return hr; @@ -2431,6 +2484,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, VARIANT *ret_val) +{ + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + HUIANODE node; + HRESULT hr; + VARIANT v; + + TRACE("%p, %p\n", iface, ret_val); + + VariantInit(ret_val); + hr = create_node_from_node_provider(prov->nested_node, 0, &v); + if (FAILED(hr) || V_VT(&v) == VT_EMPTY) + return hr; + + if (prov->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 +2525,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) @@ -2452,6 +2536,16 @@ static BOOL is_nested_node_provider(IWineUiaProvider *iface) return FALSE; }
+void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) +{ + struct uia_nested_node_provider *prov; + + assert(is_nested_node_provider(nested_prov)); + + prov = impl_from_nested_node_IWineUiaProvider(nested_prov); + prov->return_node_lres = return_node_lres; +} + static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, BOOL unwrap) { diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 91013dfc4ed..e67d98838e3 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -979,6 +979,42 @@ 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); + + set_nested_node_prov_return_node_lres(node->prov[get_node_provider_type_at_idx(node, 0)], TRUE); + hr = create_node_from_node_provider(&node->IWineUiaNode_iface, 0, &v); + set_nested_node_prov_return_node_lres(node->prov[get_node_provider_type_at_idx(node, 0)], FALSE); + 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 +1034,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 +1047,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..8b49f3e89ae 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) { @@ -213,11 +214,13 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T 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, 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; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers, int node_flags) DECLSPEC_HIDDEN; +void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) 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; @@ -231,6 +234,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_client.c | 14 ++-- dlls/uiautomationcore/uia_com_client.c | 32 +++++++-- dlls/uiautomationcore/uia_event.c | 4 +- dlls/uiautomationcore/uia_private.h | 3 +- 5 files changed, 117 insertions(+), 14 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index d00222ad1e1..a32fe9ee2e2 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_stack_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_stack_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_client.c b/dlls/uiautomationcore/uia_client.c index 5e9c51f550f..dfa993da25f 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, VARIANT *ret_val) { IWineUiaProvider *prov; HRESULT hr; @@ -2361,7 +2361,7 @@ static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *if break; }
- 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,7 +2416,7 @@ static HRESULT WINAPI uia_nested_node_provider_navigate(IWineUiaProvider *iface, 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;
@@ -2446,7 +2446,7 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface 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;
@@ -2504,7 +2504,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;
@@ -2657,14 +2657,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); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index e67d98838e3..65b02ad46b3 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1015,7 +1015,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;
@@ -1023,12 +1023,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) @@ -1039,12 +1042,33 @@ static void uia_com_focus_win_event_handler(HUIANODE node, HWND hwnd, struct uia
LIST_FOR_EACH_ENTRY(entry, &event_id_map->handlers_list, struct uia_event_handler_map_entry, handler_event_id_map_list_entry) { + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); struct uia_com_event *event;
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; + + set_nested_node_prov_return_node_lres(node_data->prov[get_node_provider_type_at_idx(node_data, 0)], TRUE); + hr = get_focus_from_node_provider((IWineUiaNode *)node, 0, &v); + set_nested_node_prov_return_node_lres(node_data->prov[get_node_provider_type_at_idx(node_data, 0)], FALSE); + 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 8b49f3e89ae..5e8cc0cec2e 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -212,6 +212,7 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c
/* 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, 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, VARIANT *ret_val) DECLSPEC_HIDDEN; @@ -221,7 +222,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; void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) 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 a32fe9ee2e2..4f1865bac66 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 65b02ad46b3..de2e8e447c3 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1084,6 +1084,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; @@ -1173,6 +1245,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 5e8cc0cec2e..02c86f4fb7d 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -238,6 +238,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; @@ -246,6 +247,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;
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/tests/uiautomation.c:
ComEventData.exp_node_desc.nested_desc[i] = &ComEventData.exp_nested_node_desc;
stack->exp_nested_node_desc[idx] = *exp_node_desc->nested_desc[i];
} elsestack->exp_node_desc[idx].nested_desc[i] = &stack->exp_nested_node_desc[idx]; break; } }
memset(&ComEventData.exp_node_desc, 0, sizeof(ComEventData.exp_node_desc));
memset(&stack->exp_node_desc[idx], 0, sizeof(stack->exp_node_desc[idx]));
- stack->exp_event_count++;
+}
+static struct node_provider_desc *pop_event_stack_event(struct ExpectedEventStack *stack) +{
- if (!stack->exp_event_count || stack->exp_event_pos >= MAX_EVENT_STACK_COUNT)
Shouldn't this be comparing to the number of actual items in the structure, rather than the theoretical maximum?
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/tests/uiautomation.c:
} else
memset(&ComEventData.exp_node_desc, 0, sizeof(ComEventData.exp_node_desc));
memset(&stack->exp_node_desc[idx], 0, sizeof(stack->exp_node_desc[idx]));
- stack->exp_event_count++;
+}
+static struct node_provider_desc *pop_event_stack_event(struct ExpectedEventStack *stack) +{
- if (!stack->exp_event_count || stack->exp_event_pos >= MAX_EVENT_STACK_COUNT)
- {
ok(0, "Failed to pop expected event from stack\n");
return NULL;
- }
- return &stack->exp_node_desc[stack->exp_event_pos++];
This appears to be a queue, not a stack.
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_client.c:
return FALSE;
}
+void set_nested_node_prov_return_node_lres(IWineUiaProvider *nested_prov, BOOL return_node_lres) +{
- struct uia_nested_node_provider *prov;
- assert(is_nested_node_provider(nested_prov));
- prov = impl_from_nested_node_IWineUiaProvider(nested_prov);
- prov->return_node_lres = return_node_lres;
+}
I don't really understand why this is state. Given that (at least so far) it only seems to bracket calls returning a variant, would it make more sense to pass it through to them as an argument?