From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 51 +++++-- dlls/uiautomationcore/uia_client.c | 8 +- dlls/uiautomationcore/uia_event.c | 157 +++++++++++++++++++++ dlls/uiautomationcore/uia_main.c | 9 -- dlls/uiautomationcore/uia_private.h | 3 + 5 files changed, 201 insertions(+), 27 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 838ff3fbe11..0ad9a883c1c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13869,6 +13869,32 @@ static void test_UiaRemoveEvent_args(HUIANODE node) ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); }
+static void test_UiaRaiseAutomationEvent_args(void) +{ + HRESULT hr; + + hr = UiaRaiseAutomationEvent(NULL, UIA_AutomationFocusChangedEventId); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Returns failure code from get_ProviderOptions. */ + initialize_provider(&Provider2, 0, NULL, TRUE); + hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + /* Windows 7 returns S_OK. */ + ok(hr == E_NOTIMPL || broken(hr == S_OK), "Unexpected hr %#lx.\n", hr); + + /* Invalid event ID - doesn't return failure code. */ + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, NULL, TRUE); + hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* + * UIA_StructureChangedEventId should use UiaRaiseStructureChangedEvent. + * No failure code is returned, however. + */ + hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_StructureChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + static void test_UiaAddEvent(void) { IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface, @@ -13908,6 +13934,7 @@ static void test_UiaAddEvent(void) /* Test valid function input arguments. */ test_UiaAddEvent_args(node); test_UiaRemoveEvent_args(node); + test_UiaRaiseAutomationEvent_args();
/* * Raise event without any registered event handlers. @@ -13915,8 +13942,7 @@ static void test_UiaAddEvent(void) 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"); + ok_method_sequence(event_seq1, "event_seq1");
/* * Register an event on a node without an HWND/RuntimeId. The event will @@ -13941,8 +13967,7 @@ static void test_UiaAddEvent(void) */ 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"); + ok_method_sequence(event_seq3, "event_seq3");
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -13976,7 +14001,7 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(uia_event_callback);
/* * Runtime ID matches so event callback is invoked, but nothing matches @@ -13991,7 +14016,7 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(uia_event_callback); set_provider_prop_override(&Provider, NULL, 0);
method_sequences_enabled = TRUE; @@ -14027,7 +14052,7 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(uia_event_callback);
/* Provider_child doesn't match the view condition, but Provider does. */ variant_init_bool(&v, FALSE); @@ -14040,7 +14065,7 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(uia_event_callback); set_provider_prop_override(&Provider_child, NULL, 0);
hr = UiaRemoveEvent(event); @@ -14061,7 +14086,7 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -14206,11 +14231,11 @@ static void test_UiaAddEvent(void) 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); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); - todo_wine CHECK_CALLED(uia_event_callback); + CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 9c5595eaf4b..a94a6b8db81 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -747,7 +747,7 @@ static HRESULT get_child_for_node(struct uia_node *node, int start_prov_idx, int return S_OK; }
-static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) +HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) { HRESULT hr; VARIANT v; @@ -835,8 +835,6 @@ static HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *o return S_OK; }
-static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition); -static BOOL uia_condition_matched(HRESULT hr); static HRESULT conditional_navigate_uia_node(struct uia_node *node, int nav_dir, struct UiaCondition *cond, HUIANODE *out_node) { @@ -3024,7 +3022,7 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback) uia_provider_callback = default_uia_provider_callback; }
-static BOOL uia_condition_matched(HRESULT hr) +BOOL uia_condition_matched(HRESULT hr) { if (hr == S_FALSE) return FALSE; @@ -3085,7 +3083,7 @@ static HRESULT uia_property_condition_check(HUIANODE node, struct UiaPropertyCon return hr; }
-static HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) +HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) { HRESULT hr;
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index beefabc83f6..e1061003d56 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -456,3 +456,160 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
return S_OK; } + +static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct uia_event *event) +{ + SAFEARRAY *out_req; + BSTR tree_struct; + HRESULT hr; + + hr = UiaGetUpdatedCache(node, &event->cache_req, NormalizeState_View, NULL, &out_req, + &tree_struct); + if (SUCCEEDED(hr)) + { + event->cback(args, out_req, tree_struct); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + + return hr; +} + +/* + * Check if the provider that raised the event matches this particular event. + */ +static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct UiaEventArgs *args, struct uia_event *event) +{ + struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId }; + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + HRESULT hr = S_OK; + + /* Can't match an event that doesn't have a runtime ID, early out. */ + if (!event->runtime_id) + return S_OK; + + IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); + if (rt_id && !uia_compare_safearrays(rt_id, event->runtime_id, UIAutomationType_IntArray)) + { + if (event->scope & TreeScope_Element) + hr = uia_event_invoke(node, args, event); + goto exit; + } + + if (!(event->scope & (TreeScope_Descendants | TreeScope_Children))) + goto exit; + + V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY; + V_ARRAY(&prop_cond.Value) = event->runtime_id; + + IWineUiaNode_AddRef(&node_data->IWineUiaNode_iface); + while (1) + { + HUIANODE node2 = NULL; + + hr = navigate_uia_node(node_data, NavigateDirection_Parent, &node2); + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + if (FAILED(hr) || !node2) + goto exit; + + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node2); + hr = uia_condition_check(node2, (struct UiaCondition *)&prop_cond); + if (FAILED(hr)) + break; + + if (uia_condition_matched(hr)) + { + hr = uia_event_invoke(node, args, event); + break; + } + + if (!(event->scope & TreeScope_Descendants)) + break; + } + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + +exit: + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + return hr; +} + +static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct UiaEventArgs *args) +{ + struct uia_event_map_entry *event_entry; + enum ProviderOptions prov_opts = 0; + struct list *cursor, *cursor2; + HUIANODE node; + SAFEARRAY *sa; + HRESULT hr; + + hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); + if (FAILED(hr)) + return hr; + + event_entry = uia_get_event_map_entry_for_event(args->EventId); + if (!event_entry) + return S_OK; + + /* + * For events raised on server-side providers, we don't want to add any + * clientside HWND providers. + */ + if (prov_opts & ProviderOptions_ServerSideProvider) + hr = create_uia_node_from_elprov(elprov, &node, FALSE); + else + hr = create_uia_node_from_elprov(elprov, &node, TRUE); + if (FAILED(hr)) + return hr; + + hr = UiaGetRuntimeId(node, &sa); + if (FAILED(hr)) + { + UiaNodeRelease(node); + return hr; + } + + LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->events_list) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + hr = uia_event_check_match(node, sa, args, event); + if (FAILED(hr)) + break; + } + + SafeArrayDestroy(sa); + UiaNodeRelease(node); + + return hr; +} + +/*********************************************************************** + * UiaRaiseAutomationEvent (uiautomationcore.@) + */ +HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *elprov, EVENTID id) +{ + const struct uia_event_info *event_info = uia_event_info_from_id(id); + struct UiaEventArgs args = { EventArgsType_Simple, id }; + HRESULT hr; + + TRACE("(%p, %d)\n", elprov, id); + + if (!elprov) + return E_INVALIDARG; + + if (!event_info || event_info->event_arg_type != EventArgsType_Simple) + { + if (!event_info) + FIXME("No event info structure for event id %d\n", id); + else + WARN("Wrong event raising function for event args type %d\n", event_info->event_arg_type); + + return S_OK; + } + + hr = uia_raise_event(elprov, &args); + if (FAILED(hr)) + return hr; + + return S_OK; +} diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index d0ab909f102..0ae6b744591 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -298,15 +298,6 @@ HRESULT WINAPI UiaGetReservedNotSupportedValue(IUnknown **value) return S_OK; }
-/*********************************************************************** - * UiaRaiseAutomationEvent (uiautomationcore.@) - */ -HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *provider, EVENTID id) -{ - FIXME("(%p, %d): stub\n", provider, id); - return S_OK; -} - /*********************************************************************** * UiaRaiseAutomationPropertyChangedEvent (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index aea9882b240..dcdaaa69f1b 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -158,8 +158,11 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c /* uia_client.c */ int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; +HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers) DECLSPEC_HIDDEN; +HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) DECLSPEC_HIDDEN; +BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN;
/* uia_com_client.c */ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;