From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 599 ++++++++++++++++++++- 1 file changed, 597 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 8494f29212c..608588c3633 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -73,6 +73,7 @@ DEFINE_EXPECT(prov_callback_base_hwnd); DEFINE_EXPECT(prov_callback_nonclient); DEFINE_EXPECT(prov_callback_proxy); DEFINE_EXPECT(prov_callback_parent_proxy); +DEFINE_EXPECT(uia_event_callback); DEFINE_EXPECT(winproc_GETOBJECT_UiaRoot); DEFINE_EXPECT(child_winproc_GETOBJECT_UiaRoot); DEFINE_EXPECT(Accessible_accNavigate); @@ -1133,6 +1134,7 @@ static struct Provider IRawElementProviderFragment IRawElementProviderFragment_iface; IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface; IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface; + IRawElementProviderAdviseEvents IRawElementProviderAdviseEvents_iface; IValueProvider IValueProvider_iface; ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface; LONG ref; @@ -1158,6 +1160,10 @@ static struct Provider struct Provider_value_pattern_data value_pattern_data; struct Provider_legacy_accessible_pattern_data legacy_acc_pattern_data; IRawElementProviderFragment *focus_prov; + IRawElementProviderFragmentRoot **embedded_frag_roots; + int embedded_frag_roots_count; + int advise_events_added_event_id; + int advise_events_removed_event_id; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); @@ -1224,8 +1230,11 @@ enum { FRAG_GET_RUNTIME_ID, FRAG_GET_FRAGMENT_ROOT, FRAG_GET_BOUNDING_RECT, + FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, FRAG_ROOT_GET_FOCUS, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, + ADVISE_EVENTS_EVENT_ADDED, + ADVISE_EVENTS_EVENT_REMOVED, };
static const char *prov_method_str[] = { @@ -1237,8 +1246,11 @@ static const char *prov_method_str[] = { "GetRuntimeId", "get_FragmentRoot", "get_BoundingRectangle", + "GetEmbeddedFragmentRoots", "GetFocus", "GetOverrideProviderForHwnd", + "AdviseEventAdded", + "AdviseEventRemoved", };
static const char *get_prov_method_str(int method) @@ -1276,6 +1288,15 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
+#define NODE_CREATE_SEQ_OPTIONAL(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, \ + { prov , FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + /* * This sequence of method calls is always used when creating an HUIANODE from * an IRawElementProviderSimple that returns an HWND from get_HostRawElementProvider. @@ -1294,6 +1315,20 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
+/* + * Node creation sequence with the first get_ProviderOptions call being optional. + * Used for event tests since each Windows version has different amounts of + * calls to get_ProviderOptions before node creation. + */ +#define NODE_CREATE_SEQ3(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, \ + { prov , PROV_GET_PROPERTY_VALUE }, \ + { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + static void flush_method_sequence(void) { HeapFree(GetProcessHeap(), 0, sequence); @@ -1593,6 +1628,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R *ppv = &This->IRawElementProviderFragmentRoot_iface; else if (IsEqualIID(riid, &IID_IRawElementProviderHwndOverride)) *ppv = &This->IRawElementProviderHwndOverride_iface; + else if (IsEqualIID(riid, &IID_IRawElementProviderAdviseEvents)) + *ppv = &This->IRawElementProviderAdviseEvents_iface; else if (IsEqualIID(riid, &IID_IValueProvider)) *ppv = &This->IValueProvider_iface; else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider)) @@ -2016,9 +2053,29 @@ static HRESULT WINAPI ProviderFragment_get_BoundingRectangle(IRawElementProvider static HRESULT WINAPI ProviderFragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface, SAFEARRAY **ret_val) { - ok(0, "unexpected call\n"); + struct Provider *This = impl_from_ProviderFragment(iface); + + add_method_call(This, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + *ret_val = NULL; - return E_NOTIMPL; + if (This->embedded_frag_roots && This->embedded_frag_roots_count) + { + SAFEARRAY *sa; + LONG idx; + + if (!(sa = SafeArrayCreateVector(VT_UNKNOWN, 0, This->embedded_frag_roots_count))) + return E_FAIL; + + for (idx = 0; idx < This->embedded_frag_roots_count; idx++) + SafeArrayPutElement(sa, &idx, (IUnknown *)This->embedded_frag_roots[idx]); + + *ret_val = sa; + } + + return S_OK; }
static HRESULT WINAPI ProviderFragment_SetFocus(IRawElementProviderFragment *iface) @@ -2166,6 +2223,66 @@ static const IRawElementProviderHwndOverrideVtbl ProviderHwndOverrideVtbl = { ProviderHwndOverride_GetOverrideProviderForHwnd, };
+static inline struct Provider *impl_from_ProviderAdviseEvents(IRawElementProviderAdviseEvents *iface) +{ + return CONTAINING_RECORD(iface, struct Provider, IRawElementProviderAdviseEvents_iface); +} + +static HRESULT WINAPI ProviderAdviseEvents_QueryInterface(IRawElementProviderAdviseEvents *iface, REFIID riid, + void **ppv) +{ + struct Provider *Provider = impl_from_ProviderAdviseEvents(iface); + return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI ProviderAdviseEvents_AddRef(IRawElementProviderAdviseEvents *iface) +{ + struct Provider *Provider = impl_from_ProviderAdviseEvents(iface); + return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI ProviderAdviseEvents_Release(IRawElementProviderAdviseEvents *iface) +{ + struct Provider *Provider = impl_from_ProviderAdviseEvents(iface); + return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI ProviderAdviseEvents_AdviseEventAdded(IRawElementProviderAdviseEvents *iface, + EVENTID event_id, SAFEARRAY *prop_ids) +{ + struct Provider *This = impl_from_ProviderAdviseEvents(iface); + + add_method_call(This, ADVISE_EVENTS_EVENT_ADDED); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + This->advise_events_added_event_id = event_id; + + return S_OK; +} + +static HRESULT WINAPI ProviderAdviseEvents_AdviseEventRemoved(IRawElementProviderAdviseEvents *iface, + EVENTID event_id, SAFEARRAY *prop_ids) +{ + struct Provider *This = impl_from_ProviderAdviseEvents(iface); + + add_method_call(This, ADVISE_EVENTS_EVENT_REMOVED); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + This->advise_events_removed_event_id = event_id; + + return S_OK; +} + +static const IRawElementProviderAdviseEventsVtbl ProviderAdviseEventsVtbl = { + ProviderAdviseEvents_QueryInterface, + ProviderAdviseEvents_AddRef, + ProviderAdviseEvents_Release, + ProviderAdviseEvents_AdviseEventAdded, + ProviderAdviseEvents_AdviseEventRemoved, +}; + static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface) { return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface); @@ -2363,6 +2480,7 @@ static struct Provider Provider = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2379,6 +2497,7 @@ static struct Provider Provider2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2395,6 +2514,7 @@ static struct Provider Provider_child = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2411,6 +2531,7 @@ static struct Provider Provider_child2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2427,6 +2548,7 @@ static struct Provider Provider_hwnd = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2443,6 +2565,7 @@ static struct Provider Provider_nc = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2460,6 +2583,7 @@ static struct Provider Provider_proxy = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2477,6 +2601,7 @@ static struct Provider Provider_proxy2 = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2494,6 +2619,7 @@ static struct Provider Provider_override = { &ProviderFragmentVtbl }, { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, + { &ProviderAdviseEventsVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2512,6 +2638,7 @@ static struct Provider Provider_override = { &ProviderFragmentVtbl }, \ { &ProviderFragmentRootVtbl }, \ { &ProviderHwndOverrideVtbl }, \ + { &ProviderAdviseEventsVtbl }, \ { &ProviderValuePatternVtbl }, \ { &ProviderLegacyIAccessiblePatternVtbl }, \ 1, \ @@ -9491,6 +9618,9 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, memset(&prov->value_pattern_data, 0, sizeof(prov->value_pattern_data)); memset(&prov->legacy_acc_pattern_data, 0, sizeof(prov->legacy_acc_pattern_data)); prov->focus_prov = NULL; + prov->embedded_frag_roots = NULL; + prov->embedded_frag_roots_count = 0; + prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0; if (initialize_nav_links) { prov->frag_root = NULL; @@ -13575,6 +13705,470 @@ static void test_UiaNodeFromFocus(void) UnregisterClassA("UiaNodeFromFocus child class", NULL); }
+static const struct prov_method_sequence event_seq1[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win8+. */ + { 0 }, +}; + +static const struct prov_method_sequence event_seq2[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ + NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ + { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { 0 }, +}; + +static const struct prov_method_sequence event_seq3[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win8+. */ + NODE_CREATE_SEQ3(&Provider), + { &Provider, FRAG_GET_RUNTIME_ID }, + { 0 }, +}; + +static const struct prov_method_sequence event_seq4[] = { + { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { 0 }, +}; + +static const struct prov_method_sequence event_seq5[] = { + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ + { &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, METHOD_TODO }, + NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ + { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { 0 }, +}; + +static const struct prov_method_sequence event_seq6[] = { + { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { 0 }, +}; + +static struct EventData { + LONG exp_lbound[2]; + LONG exp_elems[2]; + struct node_provider_desc exp_node_desc; + const WCHAR *exp_tree_struct; +} EventData; + +static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1, + struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct) +{ + EventData.exp_lbound[0] = exp_lbound0; + EventData.exp_lbound[1] = exp_lbound1; + EventData.exp_elems[0] = exp_elems0; + EventData.exp_elems[1] = exp_elems1; + if (exp_node_desc) + EventData.exp_node_desc = *exp_node_desc; + else + memset(&EventData.exp_node_desc, 0, sizeof(EventData.exp_node_desc)); + EventData.exp_tree_struct = exp_tree_struct; +} + +static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) +{ + CHECK_EXPECT(uia_event_callback); + + if (!EventData.exp_elems[0] && !EventData.exp_elems[1]) + ok(!req_data, "req_data != NULL\n"); + else + test_cache_req_sa(req_data, EventData.exp_lbound, EventData.exp_elems, &EventData.exp_node_desc); + + ok(!wcscmp(tree_struct, EventData.exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct)); +} + +struct event_test_thread_data { + HUIAEVENT event; + DWORD exp_thread_id; +}; + +static DWORD WINAPI uia_add_event_test_thread(LPVOID param) +{ + struct event_test_thread_data *data = (struct event_test_thread_data *)param; + HRESULT hr; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = UiaRemoveEvent(data->event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref); + todo_wine ok(Provider2.last_call_tid == data->exp_thread_id || + broken(Provider2.last_call_tid == GetCurrentThreadId()), "Expected method call on separate thread\n"); + todo_wine ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, + "Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id); + + CoUninitialize(); + return 0; +} + +static void test_UiaAddEvent(void) +{ + IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface, + &Provider_child2.IRawElementProviderFragmentRoot_iface }; + struct event_test_thread_data thread_data = { 0 }; + struct Provider_prop_override prop_override; + struct node_provider_desc exp_node_desc; + struct UiaPropertyCondition prop_cond; + struct UiaCacheRequest cache_req; + HUIAEVENT event; + HUIANODE node; + HANDLE thread; + HRESULT hr; + HWND hwnd; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + provider_add_child(&Provider, &Provider_child); + initialize_provider(&Provider_child_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + provider_add_child(&Provider_child, &Provider_child_child); + + /* Create a node with Provider as its only provider. */ + method_sequences_enabled = FALSE; + hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE); + VariantClear(&v); + + /* + * Raise event without any registered event handlers. + */ + method_sequences_enabled = TRUE; + hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr) && sequence_cnt) + ok_method_sequence(event_seq1, "event_seq1"); + + /* + * Register an event on a node without an HWND/RuntimeId. The event will + * be created successfully, but without any way to match a provider to + * this node, we won't be able to trigger the event handler. + */ + event = NULL; + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq2, "event_seq2"); + + /* + * Even though we raise an event on the same provider as the one our node + * currently represents, without an HWND/RuntimeId, we have no way to + * match them. The event handler will not be called. + */ + hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr) && sequence_cnt) + ok_method_sequence(event_seq3, "event_seq3"); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq4, "event_seq4"); + + /* + * Register an event on the same node again, except this time we have a + * runtimeID. Nodes returned to the event handler are normalized against + * the cache request view condition. + */ + event = NULL; + variant_init_bool(&v, TRUE); + set_property_condition(&prop_cond, UIA_IsControlElementPropertyId, &v, PropertyConditionFlags_None); + set_cache_request(&cache_req, (struct UiaCondition *)&prop_cond, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq2, "event_seq2"); + + /* Event callback is now invoked since we can match by runtime ID. */ + method_sequences_enabled = FALSE; + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_event_callback); + + /* + * Runtime ID matches so event callback is invoked, but nothing matches + * the cache request view condition, so the callback will be passed a + * NULL cache request. + */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider, &prop_override, 1); + + set_event_data(0, 0, 0, 0, &exp_node_desc, L""); + SET_EXPECT(uia_event_callback); + hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_event_callback); + set_provider_prop_override(&Provider, NULL, 0); + + method_sequences_enabled = TRUE; + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq4, "event_seq4"); + + /* Create an event with TreeScope_Children. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Children, NULL, 0, &cache_req, + &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq5, "event_seq5"); + + /* + * Only TreeScope_Children and not TreeScope_Element, handler won't be + * called. + */ + method_sequences_enabled = FALSE; + hr = UiaRaiseAutomationEvent(&Provider.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Provider_child_child is not a direct child, handler won't be called. */ + hr = UiaRaiseAutomationEvent(&Provider_child_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Raised an event on Provider_child, handler will be called. */ + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + hr = UiaRaiseAutomationEvent(&Provider_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_event_callback); + + /* Provider_child doesn't match the view condition, but Provider does. */ + variant_init_bool(&v, FALSE); + set_property_override(&prop_override, UIA_IsControlElementPropertyId, &v); + set_provider_prop_override(&Provider_child, &prop_override, 1); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + hr = UiaRaiseAutomationEvent(&Provider_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_event_callback); + set_provider_prop_override(&Provider_child, NULL, 0); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + /* Create an event with TreeScope_Descendants. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, + &cache_req, &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + + /* Raised an event on Provider_child_child. */ + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child_child", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + hr = UiaRaiseAutomationEvent(&Provider_child_child.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_event_callback); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + CoUninitialize(); + + /* + * When adding an event, each node provider's fragment root is queried + * for, and if one is retrieved, it's IRawElementProviderAdviseEvents + * interface is used. On Win10v1809+, ProviderOptions_UseComThreading is + * respected for these interfaces. Set Provider2 as the fragment root here + * to test this. + */ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider2, ProviderOptions_UseComThreading | ProviderOptions_ServerSideProvider, NULL, TRUE); + Provider.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface; + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, + &cache_req, &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + todo_wine ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref); + ok(!Provider.advise_events_added_event_id, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); + todo_wine ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); + + thread_data.exp_thread_id = GetCurrentThreadId(); + thread_data.event = event; + thread = CreateThread(NULL, 0, uia_add_event_test_thread, (void *)&thread_data, 0, NULL); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + MSG msg; + + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + CloseHandle(thread); + + /* Test retrieving AdviseEvents on embedded fragment roots. */ + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, FALSE); + initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, FALSE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + Provider.embedded_frag_roots = embedded_roots; + Provider.embedded_frag_roots_count = ARRAY_SIZE(embedded_roots); + Provider_child.frag_root = &Provider_child.IRawElementProviderFragmentRoot_iface; + Provider_child2.frag_root = &Provider_child2.IRawElementProviderFragmentRoot_iface; + + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, + &cache_req, &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + todo_wine ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); + todo_wine ok(Provider_child.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + "Unexpected advise event added, event ID %d\n", Provider_child.advise_events_added_event_id); + todo_wine ok(Provider_child2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + "Unexpected advise event added, event ID %d\n", Provider_child2.advise_events_added_event_id); + + method_sequences_enabled = TRUE; + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref); + if (SUCCEEDED(hr)) + ok_method_sequence(event_seq6, "event_seq6"); + + UiaNodeRelease(node); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE); + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + initialize_provider(&Provider_child2, ProviderOptions_ServerSideProvider, NULL, TRUE); + + hwnd = create_test_hwnd("UiaAddEvent test class"); + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* Set clientside providers for our test window and the desktop. */ + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); + Provider_proxy.frag_root = Provider_hwnd.frag_root = Provider_nc.frag_root = NULL; + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + + set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd); + provider_add_child(&Provider_nc, &Provider_nc2); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + prov_root = &Provider.IRawElementProviderSimple_iface; + Provider.ignore_hwnd_prop = TRUE; + + method_sequences_enabled = FALSE; + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = UiaGetRootNode(&node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + ok(Provider_proxy.ref == 2, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 2, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + /* Register an event on the desktop HWND. */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Children, NULL, 0, + &cache_req, &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + + /* + * Raising an event on a serverside provider results in no clientside + * providers being added for the HWND, which means we won't match this + * event. + */ + Provider_hwnd2.prov_opts = ProviderOptions_ServerSideProvider; + hr = UiaRaiseAutomationEvent(&Provider_hwnd2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Now that Provider has clientside providers, the event will be matched. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Main", L"Provider", FALSE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", TRUE); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", FALSE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + Provider_hwnd2.prov_opts = ProviderOptions_ClientSideProvider; + hr = UiaRaiseAutomationEvent(&Provider_hwnd2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(uia_event_callback); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + UiaNodeRelease(node); + + method_sequences_enabled = TRUE; + CoUninitialize(); + UiaRegisterProviderCallback(NULL); + DestroyWindow(hwnd); + UnregisterClassA("UiaAddEvent test class", NULL); +} + /* * Once a process returns a UI Automation provider with * UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This @@ -13645,6 +14239,7 @@ START_TEST(uiautomation) test_default_clientside_providers(); test_UiaGetRootNode(); test_UiaNodeFromFocus(); + test_UiaAddEvent(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/Makefile.in | 1 + dlls/uiautomationcore/tests/uiautomation.c | 79 ++++++++--- dlls/uiautomationcore/uia_classes.idl | 9 ++ dlls/uiautomationcore/uia_client.c | 11 -- dlls/uiautomationcore/uia_event.c | 148 +++++++++++++++++++++ dlls/uiautomationcore/uia_ids.c | 19 +++ dlls/uiautomationcore/uia_private.h | 1 + 7 files changed, 237 insertions(+), 31 deletions(-) create mode 100644 dlls/uiautomationcore/uia_event.c
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 28d814e6ee6..8156807c334 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -7,6 +7,7 @@ EXTRADLLFLAGS = -Wb,--prefer-native C_SRCS = \ uia_client.c \ uia_com_client.c \ + uia_event.c \ uia_ids.c \ uia_main.c \ uia_provider.c diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 608588c3633..96ae541c439 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13813,6 +13813,45 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) return 0; }
+static void test_UiaAddEvent_args(HUIANODE node) +{ + struct UiaCacheRequest cache_req; + HUIAEVENT event; + HRESULT hr; + + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + + /* NULL node. */ + hr = UiaAddEvent(NULL, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL event callback. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, NULL, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL cache request. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, NULL, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL event handle. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Event IDs aren't checked for validity, 1 is not a valid UIA event ID. */ + event = NULL; + hr = UiaAddEvent(node, 1, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + static void test_UiaAddEvent(void) { IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface, @@ -13849,6 +13888,9 @@ static void test_UiaAddEvent(void) check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE); VariantClear(&v);
+ /* Test valid function input arguments. */ + test_UiaAddEvent_args(node); + /* * Raise event without any registered event handlers. */ @@ -13869,11 +13911,10 @@ static void test_UiaAddEvent(void) AutomationElementMode_Full); hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq2, "event_seq2"); + ok_method_sequence(event_seq2, "event_seq2");
/* * Even though we raise an event on the same provider as the one our node @@ -13905,11 +13946,10 @@ static void test_UiaAddEvent(void) Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq2, "event_seq2"); + ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */ method_sequences_enabled = FALSE; @@ -13947,11 +13987,10 @@ static void test_UiaAddEvent(void) /* Create an event with TreeScope_Children. */ hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Children, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq5, "event_seq5"); + ok_method_sequence(event_seq5, "event_seq5");
/* * Only TreeScope_Children and not TreeScope_Element, handler won't be @@ -13995,8 +14034,8 @@ static void test_UiaAddEvent(void) /* Create an event with TreeScope_Descendants. */ hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
/* Raised an event on Provider_child_child. */ @@ -14028,8 +14067,8 @@ static void test_UiaAddEvent(void) Provider.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface; hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); todo_wine ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref); ok(!Provider.advise_events_added_event_id, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); @@ -14063,8 +14102,8 @@ static void test_UiaAddEvent(void)
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); @@ -14126,8 +14165,8 @@ static void test_UiaAddEvent(void) AutomationElementMode_Full); hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Children, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n");
/* * Raising an event on a serverside provider results in no clientside diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index b727184ff37..83ff63fdd70 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -54,6 +54,15 @@ library UIA_wine_private { importlib("stdole2.tlb");
+ [ + object, + uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba), + pointer_default(unique), + ] + interface IWineUiaEvent : IUnknown + { + } + [ object, uuid(57865755-6c05-4522-98df-4ca658b768ef), diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index a2f464ae4df..c8b369fa65b 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3513,17 +3513,6 @@ exit: return hr; }
-/*********************************************************************** - * UiaAddEvent (uiautomationcore.@) - */ -HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, - PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) -{ - FIXME("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, - cache_req, huiaevent); - return E_NOTIMPL; -} - /*********************************************************************** * UiaRemoveEvent (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c new file mode 100644 index 00000000000..e1d2585c056 --- /dev/null +++ b/dlls/uiautomationcore/uia_event.c @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Connor McAdams for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "uia_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(uiautomation); + +struct uia_event +{ + IWineUiaEvent IWineUiaEvent_iface; + LONG ref; + + SAFEARRAY *runtime_id; + int event_id; + int scope; + + UiaEventCallback *cback; +}; + +/* + * IWineUiaEvent interface. + */ +static inline struct uia_event *impl_from_IWineUiaEvent(IWineUiaEvent *iface) +{ + return CONTAINING_RECORD(iface, struct uia_event, IWineUiaEvent_iface); +} + +static HRESULT WINAPI uia_event_QueryInterface(IWineUiaEvent *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IWineUiaEvent) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IWineUiaEvent_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_event_AddRef(IWineUiaEvent *iface) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + ULONG ref = InterlockedIncrement(&event->ref); + + TRACE("%p, refcount %ld\n", event, ref); + return ref; +} + +static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + ULONG ref = InterlockedDecrement(&event->ref); + + TRACE("%p, refcount %ld\n", event, ref); + if (!ref) + { + SafeArrayDestroy(event->runtime_id); + heap_free(event); + } + + return ref; +} + +static const IWineUiaEventVtbl uia_event_vtbl = { + uia_event_QueryInterface, + uia_event_AddRef, + uia_event_Release, +}; + +static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, + SAFEARRAY *runtime_id) +{ + struct uia_event *event = heap_alloc_zero(sizeof(*event)); + + *out_event = NULL; + if (!event) + return E_OUTOFMEMORY; + + event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl; + event->ref = 1; + event->runtime_id = runtime_id; + event->event_id = event_id; + event->scope = scope; + event->cback = cback; + + *out_event = event; + return S_OK; +} + +/*********************************************************************** + * UiaAddEvent (uiautomationcore.@) + */ +HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, + PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) +{ + const struct uia_event_info *event_info = uia_event_info_from_id(event_id); + struct uia_event *event; + SAFEARRAY *sa; + HRESULT hr; + + TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, + cache_req, huiaevent); + + if (!huianode || !callback || !cache_req || !huiaevent) + return E_INVALIDARG; + + if (!event_info) + WARN("No event information for event ID %d\n", event_id); + + *huiaevent = NULL; + if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged)) + { + FIXME("Property changed event registration currently unimplemented\n"); + return E_NOTIMPL; + } + + hr = UiaGetRuntimeId(huianode, &sa); + if (FAILED(hr)) + return hr; + + hr = create_uia_event(&event, event_id, scope, callback, sa); + if (FAILED(hr)) + { + SafeArrayDestroy(sa); + return hr; + } + + *huiaevent = (HUIAEVENT)event; + + return S_OK; +} diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index d23ba14c333..01e52f6f45a 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -413,6 +413,17 @@ static const struct uia_event_info default_uia_events[] = { EventArgsType_Simple, }, };
+static const int event_id_idx[] = { + 0x23, 0x1d, 0x0d, 0x0b, 0x19, 0x06, 0x07, 0x0f, + 0x0a, 0x1e, 0x1b, 0x1a, 0x22, 0x00, 0x18, 0x10, + 0x01, 0x20, 0x08, 0x17, 0x15, 0x13, 0x0e, 0x0c, + 0x14, 0x09, 0x03, 0x21, 0x12, 0x16, 0x05, 0x1c, + 0x02, 0x11, 0x04, 0x1f, +}; + +#define EVENT_ID_MIN 20000 +#define EVENT_ID_MAX (EVENT_ID_MIN + ARRAY_SIZE(default_uia_events)) + static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid) { struct uia_event_info *event; @@ -424,6 +435,14 @@ static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid) return NULL; }
+const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) +{ + if ((event_id < EVENT_ID_MIN) || (event_id > EVENT_ID_MAX)) + return NULL; + + return &default_uia_events[event_id_idx[event_id - EVENT_ID_MIN]]; +} + /* Sorted by GUID. */ static const struct uia_pattern_info default_uia_patterns[] = { { &ScrollItem_Pattern_GUID, UIA_ScrollItemPatternId, diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 8bc0436697d..d3f46874f01 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -147,6 +147,7 @@ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; +const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) DECLSPEC_HIDDEN; const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) DECLSPEC_HIDDEN; const struct uia_control_type_info *uia_control_type_info_from_id(CONTROLTYPEID control_type_id) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 37 ++++++++++++++-------- dlls/uiautomationcore/uia_client.c | 9 ------ dlls/uiautomationcore/uia_event.c | 24 ++++++++++++++ 3 files changed, 47 insertions(+), 23 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 96ae541c439..b2485d86b89 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13801,7 +13801,7 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = UiaRemoveEvent(data->event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref); todo_wine ok(Provider2.last_call_tid == data->exp_thread_id || @@ -13849,7 +13849,18 @@ static void test_UiaAddEvent_args(HUIANODE node) ok(!!event, "event == NULL\n");
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + +static void test_UiaRemoveEvent_args(HUIANODE node) +{ + HRESULT hr; + + hr = UiaRemoveEvent(NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = UiaRemoveEvent((HUIAEVENT)node); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); }
static void test_UiaAddEvent(void) @@ -13890,6 +13901,7 @@ static void test_UiaAddEvent(void)
/* Test valid function input arguments. */ test_UiaAddEvent_args(node); + test_UiaRemoveEvent_args(node);
/* * Raise event without any registered event handlers. @@ -13927,10 +13939,9 @@ static void test_UiaAddEvent(void) ok_method_sequence(event_seq3, "event_seq3");
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq4, "event_seq4"); + ok_method_sequence(event_seq4, "event_seq4");
/* * Register an event on the same node again, except this time we have a @@ -13979,10 +13990,9 @@ static void test_UiaAddEvent(void)
method_sequences_enabled = TRUE; hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq4, "event_seq4"); + ok_method_sequence(event_seq4, "event_seq4");
/* Create an event with TreeScope_Children. */ hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Children, NULL, 0, &cache_req, @@ -14028,7 +14038,7 @@ static void test_UiaAddEvent(void) set_provider_prop_override(&Provider_child, NULL, 0);
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
/* Create an event with TreeScope_Descendants. */ @@ -14048,7 +14058,7 @@ static void test_UiaAddEvent(void) todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
CoUninitialize(); @@ -14116,12 +14126,11 @@ static void test_UiaAddEvent(void)
method_sequences_enabled = TRUE; hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq6, "event_seq6"); + ok_method_sequence(event_seq6, "event_seq6");
UiaNodeRelease(node); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); @@ -14198,7 +14207,7 @@ static void test_UiaAddEvent(void) todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); UiaNodeRelease(node);
method_sequences_enabled = TRUE; diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index c8b369fa65b..ced728428a3 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3513,15 +3513,6 @@ exit: return hr; }
-/*********************************************************************** - * UiaRemoveEvent (uiautomationcore.@) - */ -HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) -{ - FIXME("(%p): stub\n", huiaevent); - return E_NOTIMPL; -} - /*********************************************************************** * UiaEventAddWindow (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index e1d2585c056..faeb689a3b6 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -84,6 +84,14 @@ static const IWineUiaEventVtbl uia_event_vtbl = { uia_event_Release, };
+static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) +{ + if (!iface || (iface->lpVtbl != &uia_event_vtbl)) + return NULL; + + return CONTAINING_RECORD(iface, struct uia_event, IWineUiaEvent_iface); +} + static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, SAFEARRAY *runtime_id) { @@ -146,3 +154,19 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback
return S_OK; } + +/*********************************************************************** + * UiaRemoveEvent (uiautomationcore.@) + */ +HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) +{ + struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); + + TRACE("(%p)\n", event); + + if (!event) + return E_INVALIDARG; + + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + return S_OK; +}
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 30 ++++---- dlls/uiautomationcore/uia_classes.idl | 2 + dlls/uiautomationcore/uia_client.c | 77 ++++++++++++++++++++ dlls/uiautomationcore/uia_event.c | 82 ++++++++++++++++++---- dlls/uiautomationcore/uia_private.h | 21 ++++++ 5 files changed, 184 insertions(+), 28 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b2485d86b89..e3f555d9fe1 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13713,12 +13713,12 @@ static const struct prov_method_sequence event_seq1[] = { static const struct prov_method_sequence event_seq2[] = { { &Provider, FRAG_GET_RUNTIME_ID }, { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT }, { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_ADDED }, { 0 }, };
@@ -13731,25 +13731,25 @@ static const struct prov_method_sequence event_seq3[] = { };
static const struct prov_method_sequence event_seq4[] = { - { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_REMOVED }, { 0 }, };
static const struct prov_method_sequence event_seq5[] = { { &Provider, FRAG_GET_RUNTIME_ID }, { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT }, { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ { &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, METHOD_TODO }, NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_ADDED }, { 0 }, };
static const struct prov_method_sequence event_seq6[] = { - { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_REMOVED }, { &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, { &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, { 0 }, @@ -13806,7 +13806,7 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref); todo_wine ok(Provider2.last_call_tid == data->exp_thread_id || broken(Provider2.last_call_tid == GetCurrentThreadId()), "Expected method call on separate thread\n"); - todo_wine ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id);
CoUninitialize(); @@ -13925,7 +13925,7 @@ static void test_UiaAddEvent(void) &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); - todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq2, "event_seq2");
/* @@ -13959,7 +13959,7 @@ static void test_UiaAddEvent(void) &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); - todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */ @@ -13999,7 +13999,7 @@ static void test_UiaAddEvent(void) &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); - todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq5, "event_seq5");
/* @@ -14046,7 +14046,7 @@ static void test_UiaAddEvent(void) &cache_req, &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); - todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
/* Raised an event on Provider_child_child. */ init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); @@ -14080,9 +14080,9 @@ static void test_UiaAddEvent(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); - todo_wine ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref); + ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref); ok(!Provider.advise_events_added_event_id, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); - todo_wine ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id);
thread_data.exp_thread_id = GetCurrentThreadId(); @@ -14114,10 +14114,10 @@ static void test_UiaAddEvent(void) &cache_req, &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); - todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - todo_wine ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); todo_wine ok(Provider_child.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider_child.advise_events_added_event_id); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 83ff63fdd70..3697a4024f5 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -61,6 +61,7 @@ library UIA_wine_private ] interface IWineUiaEvent : IUnknown { + HRESULT advise_events([in]BOOL advise_added); }
[ @@ -76,6 +77,7 @@ library UIA_wine_private 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 attach_event([in]LONG_PTR huiaevent); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index ced728428a3..2c2884907b5 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -462,6 +462,21 @@ static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT return hr; }
+static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEVENT huiaevent) +{ + IWineUiaProvider *prov; + HRESULT hr; + + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_attach_event(prov, (LONG_PTR)huiaevent); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1285,6 +1300,22 @@ exit: return hr; }
+HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) +{ + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + HRESULT hr = S_OK; + int i; + + for (i = 0; i < node_data->prov_count; i++) + { + hr = attach_event_to_node_provider(&node_data->IWineUiaNode_iface, i, (HUIAEVENT)event); + if (FAILED(hr)) + return hr; + } + + return hr; +} + /* * IWineUiaProvider interface. */ @@ -1813,6 +1844,44 @@ static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *o return hr; }
+static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + struct uia_event *event = (struct uia_event *)huiaevent; + IRawElementProviderFragmentRoot *elroot; + IRawElementProviderFragment *elfrag; + HRESULT hr; + + TRACE("%p, %#Ix\n", iface, huiaevent); + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag); + if (FAILED(hr)) + return S_OK; + + hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot); + IRawElementProviderFragment_Release(elfrag); + if (FAILED(hr)) + return hr; + + if (elroot) + { + IRawElementProviderAdviseEvents *advise_events = NULL; + + hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, + (void **)&advise_events); + IRawElementProviderFragmentRoot_Release(elroot); + if (advise_events) + { + hr = uia_event_add_provider_event_adviser(advise_events, event); + IRawElementProviderAdviseEvents_Release(advise_events); + if (FAILED(hr)) + return hr; + } + } + + return S_OK; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1822,6 +1891,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_has_parent, uia_provider_navigate, uia_provider_get_focus, + uia_provider_attach_event, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2246,6 +2316,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface return S_OK; }
+static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) +{ + FIXME("%p, %#Ix: stub\n", iface, huiaevent); + return E_NOTIMPL; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2255,6 +2331,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_has_parent, uia_nested_node_provider_navigate, uia_nested_node_provider_get_focus, + uia_nested_node_provider_attach_event, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index faeb689a3b6..f627e1813cd 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -22,18 +22,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
-struct uia_event -{ - IWineUiaEvent IWineUiaEvent_iface; - LONG ref; - - SAFEARRAY *runtime_id; - int event_id; - int scope; - - UiaEventCallback *cback; -}; - /* * IWineUiaEvent interface. */ @@ -71,17 +59,55 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) TRACE("%p, refcount %ld\n", event, ref); if (!ref) { + int i; + SafeArrayDestroy(event->runtime_id); + for (i = 0; i < event->event_advisers_count; i++) + IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + heap_free(event->event_advisers); heap_free(event); }
return ref; }
+static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_added) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + HRESULT hr; + int i; + + TRACE("%p, %d\n", event, advise_added); + + for (i = 0; i < event->event_advisers_count; i++) + { + IRawElementProviderAdviseEvents *adviser = event->event_advisers[i]; + + if (advise_added) + hr = IRawElementProviderAdviseEvents_AdviseEventAdded(adviser, event->event_id, NULL); + else + hr = IRawElementProviderAdviseEvents_AdviseEventRemoved(adviser, event->event_id, NULL); + if (FAILED(hr)) + return hr; + } + + /* Once we've advised of removal, no need to keep the advisers around. */ + if (!advise_added) + { + for (i = 0; i < event->event_advisers_count; i++) + IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + heap_free(event->event_advisers); + event->event_advisers_count = event->event_advisers_arr_size = 0; + } + + return S_OK; +} + static const IWineUiaEventVtbl uia_event_vtbl = { uia_event_QueryInterface, uia_event_AddRef, uia_event_Release, + uia_event_advise_events, };
static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) @@ -112,6 +138,19 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int return S_OK; }
+HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) +{ + if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, + event->event_advisers_count + 1, sizeof(*event->event_advisers))) + return E_OUTOFMEMORY; + + event->event_advisers[event->event_advisers_count] = advise_events; + IRawElementProviderAdviseEvents_AddRef(advise_events); + event->event_advisers_count++; + + return S_OK; +} + /*********************************************************************** * UiaAddEvent (uiautomationcore.@) */ @@ -150,9 +189,21 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback return hr; }
+ hr = attach_event_to_uia_node(huianode, event); + if (FAILED(hr)) + goto exit; + + hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, TRUE); + if (FAILED(hr)) + goto exit; + *huiaevent = (HUIAEVENT)event;
- return S_OK; +exit: + if (FAILED(hr)) + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + + return hr; }
/*********************************************************************** @@ -161,12 +212,17 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) { struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); + HRESULT hr;
TRACE("(%p)\n", event);
if (!event) return E_INVALIDARG;
+ hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE); IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + if (FAILED(hr)) + WARN("advise_events failed with hr %#lx\n", hr); + return S_OK; } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index d3f46874f01..9ee5c30aeef 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -93,6 +93,22 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); }
+struct uia_event +{ + IWineUiaEvent IWineUiaEvent_iface; + LONG ref; + + SAFEARRAY *runtime_id; + int event_id; + int scope; + + IRawElementProviderAdviseEvents **event_advisers; + int event_advisers_count; + SIZE_T event_advisers_arr_size; + + UiaEventCallback *cback; +}; + static inline void variant_init_bool(VARIANT *v, BOOL val) { V_VT(v) = VT_BOOL; @@ -139,12 +155,17 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; +HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers) DECLSPEC_HIDDEN;
/* uia_com_client.c */ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
+/* uia_event.c */ +HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, + struct uia_event *event) DECLSPEC_HIDDEN; + /* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) DECLSPEC_HIDDEN;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=132950
Your paranoid android.
=== w10pro64_ar (64 bit report) ===
uiautomationcore: uiautomation.c:13913: Test failed: event_seq1: incomplete sequence: expected nothing, got Provider_get_FragmentRoot uiautomation.c:13939: Test failed: event_seq3: 4: expected Provider_get_HostRawElementProvider, got Provider_get_FragmentRoot uiautomation.c:13939: Test failed: event_seq3: 5: expected Provider_GetPropertyValue, got Provider_get_HostRawElementProvider uiautomation.c:13939: Test failed: event_seq3: 6: expected Provider_Navigate, got Provider_GetPropertyValue uiautomation.c:13939: Test failed: event_seq3: 8: expected Provider_GetRuntimeId, got Provider_get_FragmentRoot uiautomation.c:13939: Test failed: event_seq3: incomplete sequence: expected nothing, got Provider_get_ProviderOptions