-- v5: uiautomationcore: Call IRawElementProviderAdviseEvents methods when events are added or removed.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_ids.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index d23ba14c333..db4bdccc24a 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -331,7 +331,7 @@ static const struct uia_prop_info *uia_prop_info_from_guid(const GUID *guid)
const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) { - if ((prop_id < PROP_ID_MIN) || (prop_id > PROP_ID_MAX)) + if ((prop_id < PROP_ID_MIN) || (prop_id >= PROP_ID_MAX)) return NULL;
return &default_uia_properties[prop_id_idx[prop_id - PROP_ID_MIN]]; @@ -520,7 +520,7 @@ static const struct uia_pattern_info *uia_pattern_info_from_guid(const GUID *gui
const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) { - if ((pattern_id < PATTERN_ID_MIN) || (pattern_id > PATTERN_ID_MAX)) + if ((pattern_id < PATTERN_ID_MIN) || (pattern_id >= PATTERN_ID_MAX)) return NULL;
return &default_uia_patterns[pattern_id_idx[pattern_id - PATTERN_ID_MIN]]; @@ -596,7 +596,7 @@ static const struct uia_control_type_info *uia_control_type_info_from_guid(const
const struct uia_control_type_info *uia_control_type_info_from_id(CONTROLTYPEID control_type_id) { - if ((control_type_id < CONTROL_TYPE_ID_MIN) || (control_type_id > CONTROL_TYPE_ID_MAX)) + if ((control_type_id < CONTROL_TYPE_ID_MIN) || (control_type_id >= CONTROL_TYPE_ID_MAX)) return NULL;
return &default_uia_control_types[control_type_id_idx[control_type_id - CONTROL_TYPE_ID_MIN]];
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 605 ++++++++++++++++++++- 1 file changed, 603 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 8494f29212c..6b5d739ca47 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,476 @@ 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+. */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Only called on Arabic version of Win10+. */ + { 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 }, + /* The following four methods are only called on Arabic versions of Win10+. */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, + { &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 +14245,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 6b5d739ca47..475955e251e 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13819,6 +13819,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, @@ -13855,6 +13894,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. */ @@ -13875,11 +13917,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 @@ -13911,11 +13952,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; @@ -13953,11 +13993,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 @@ -14001,8 +14040,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. */ @@ -14034,8 +14073,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); @@ -14069,8 +14108,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); @@ -14132,8 +14171,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 db4bdccc24a..434494a8f2b 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 475955e251e..596104484ac 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13807,7 +13807,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 || @@ -13855,7 +13855,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) @@ -13896,6 +13907,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. @@ -13933,10 +13945,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 @@ -13985,10 +13996,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, @@ -14034,7 +14044,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. */ @@ -14054,7 +14064,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(); @@ -14122,12 +14132,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); @@ -14204,7 +14213,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 596104484ac..2dd35199186 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13714,12 +13714,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 }, };
@@ -13737,25 +13737,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 }, @@ -13812,7 +13812,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(); @@ -13931,7 +13931,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");
/* @@ -13965,7 +13965,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. */ @@ -14005,7 +14005,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");
/* @@ -14052,7 +14052,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); @@ -14086,9 +14086,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(); @@ -14120,10 +14120,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..4a14fe8e48a 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; + + hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, + (void **)&advise_events); + IRawElementProviderFragmentRoot_Release(elroot); + if (SUCCEEDED(hr)) + { + 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;
This merge request was approved by Esme Povirk.