2 * Copyright 2022 Connor McAdams for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "uia_private.h"
22 #include "wine/debug.h"
23 #include "wine/heap.h"
25 #include "wine/iaccessible2.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation
);
29 DEFINE_GUID(SID_AccFromDAWrapper
, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
31 static void variant_init_i4(VARIANT
*v
, int val
)
37 static void variant_init_bool(VARIANT
*v
, BOOL val
)
40 V_BOOL(v
) = val
? VARIANT_TRUE
: VARIANT_FALSE
;
43 static BOOL
msaa_check_acc_state(IAccessible
*acc
, VARIANT cid
, ULONG flag
)
49 hr
= IAccessible_get_accState(acc
, cid
, &v
);
50 if (SUCCEEDED(hr
) && V_VT(&v
) == VT_I4
&& (V_I4(&v
) & flag
))
56 static IAccessible2
*msaa_acc_get_ia2(IAccessible
*acc
)
58 IServiceProvider
*serv_prov
;
59 IAccessible2
*ia2
= NULL
;
62 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
65 hr
= IServiceProvider_QueryService(serv_prov
, &IID_IAccessible2
, &IID_IAccessible2
, (void **)&ia2
);
66 IServiceProvider_Release(serv_prov
);
67 if (SUCCEEDED(hr
) && ia2
)
71 hr
= IAccessible_QueryInterface(acc
, &IID_IAccessible2
, (void **)&ia2
);
72 if (SUCCEEDED(hr
) && ia2
)
78 static IAccessible
*msaa_acc_da_unwrap(IAccessible
*acc
)
84 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void**)&sp
);
87 hr
= IServiceProvider_QueryService(sp
, &SID_AccFromDAWrapper
, &IID_IAccessible
, (void**)&acc2
);
88 IServiceProvider_Release(sp
);
91 if (SUCCEEDED(hr
) && acc2
)
94 IAccessible_AddRef(acc
);
99 * Compare role, state, child count, and location properties of the two
100 * IAccessibles. If all four are successfully retrieved and are equal, this is
101 * considered a match.
103 static HRESULT
msaa_acc_prop_match(IAccessible
*acc
, IAccessible
*acc2
)
105 BOOL role_match
, state_match
, child_count_match
, location_match
;
106 LONG child_count
[2], left
[2], top
[2], width
[2], height
[2];
110 role_match
= state_match
= child_count_match
= location_match
= FALSE
;
111 variant_init_i4(&cid
, CHILDID_SELF
);
112 hr
= IAccessible_get_accRole(acc
, cid
, &v
);
113 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
116 hr
= IAccessible_get_accRole(acc2
, cid
, &v2
);
117 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
119 if (V_I4(&v
) != V_I4(&v2
))
127 hr
= IAccessible_get_accState(acc
, cid
, &v
);
128 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
131 hr
= IAccessible_get_accState(acc2
, cid
, &v2
);
132 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
134 if (V_I4(&v
) != V_I4(&v2
))
141 hr
= IAccessible_get_accChildCount(acc
, &child_count
[0]);
142 hr2
= IAccessible_get_accChildCount(acc2
, &child_count
[1]);
143 if (SUCCEEDED(hr
) && SUCCEEDED(hr2
))
145 if (child_count
[0] != child_count
[1])
148 child_count_match
= TRUE
;
151 hr
= IAccessible_accLocation(acc
, &left
[0], &top
[0], &width
[0], &height
[0], cid
);
154 hr
= IAccessible_accLocation(acc2
, &left
[1], &top
[1], &width
[1], &height
[1], cid
);
157 if ((left
[0] != left
[1]) || (top
[0] != top
[1]) || (width
[0] != width
[1]) ||
158 (height
[0] != height
[1]))
161 location_match
= TRUE
;
165 if (role_match
&& state_match
&& child_count_match
&& location_match
)
171 static BOOL
msaa_acc_compare(IAccessible
*acc
, IAccessible
*acc2
)
173 IAccessible2
*ia2
[2] = { NULL
, NULL
};
174 IUnknown
*unk
, *unk2
;
175 BOOL matched
= FALSE
;
181 acc
= msaa_acc_da_unwrap(acc
);
182 acc2
= msaa_acc_da_unwrap(acc2
);
183 IAccessible_QueryInterface(acc
, &IID_IUnknown
, (void**)&unk
);
184 IAccessible_QueryInterface(acc2
, &IID_IUnknown
, (void**)&unk2
);
191 ia2
[0] = msaa_acc_get_ia2(acc
);
192 ia2
[1] = msaa_acc_get_ia2(acc2
);
193 if (!ia2
[0] != !ia2
[1])
197 hr
= IAccessible2_get_uniqueID(ia2
[0], &unique_id
[0]);
200 hr
= IAccessible2_get_uniqueID(ia2
[1], &unique_id
[1]);
203 if (unique_id
[0] == unique_id
[1])
211 hr
= msaa_acc_prop_match(acc
, acc2
);
217 variant_init_i4(&cid
, CHILDID_SELF
);
218 hr
= IAccessible_get_accName(acc
, cid
, &name
[0]);
221 hr
= IAccessible_get_accName(acc2
, cid
, &name
[1]);
224 if (!name
[0] && !name
[1])
226 else if (!name
[0] || !name
[1])
230 if (!wcscmp(name
[0], name
[1]))
236 SysFreeString(name
[1]);
239 SysFreeString(name
[0]);
243 IUnknown_Release(unk
);
244 IUnknown_Release(unk2
);
245 IAccessible_Release(acc
);
246 IAccessible_Release(acc2
);
248 IAccessible2_Release(ia2
[0]);
250 IAccessible2_Release(ia2
[1]);
255 static HRESULT
msaa_acc_get_parent(IAccessible
*acc
, IAccessible
**parent
)
257 IDispatch
*disp
= NULL
;
261 hr
= IAccessible_get_accParent(acc
, &disp
);
262 if (FAILED(hr
) || !disp
)
265 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void**)parent
);
266 IDispatch_Release(disp
);
270 #define DIR_FORWARD 0
271 #define DIR_REVERSE 1
272 static HRESULT
msaa_acc_get_next_child(IAccessible
*acc
, LONG start_pos
, LONG direction
,
273 IAccessible
**child
, LONG
*child_id
, LONG
*child_pos
, BOOL check_visible
)
275 LONG child_count
, cur_pos
;
285 hr
= IAccessible_get_accChildCount(acc
, &child_count
);
286 if (FAILED(hr
) || (cur_pos
> child_count
))
289 variant_init_i4(&cid
, cur_pos
);
290 hr
= IAccessible_get_accChild(acc
, cid
, &disp
);
296 if (!check_visible
|| !msaa_check_acc_state(acc
, cid
, STATE_SYSTEM_INVISIBLE
))
299 *child_id
= *child_pos
= cur_pos
;
300 IAccessible_AddRef(acc
);
306 IAccessible
*acc_child
= NULL
;
308 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void **)&acc_child
);
309 IDispatch_Release(disp
);
313 variant_init_i4(&cid
, CHILDID_SELF
);
314 if (!check_visible
|| !msaa_check_acc_state(acc_child
, cid
, STATE_SYSTEM_INVISIBLE
))
317 *child_id
= CHILDID_SELF
;
318 *child_pos
= cur_pos
;
322 IAccessible_Release(acc_child
);
325 if (direction
== DIR_FORWARD
)
330 if ((cur_pos
> child_count
) || (cur_pos
<= 0))
337 static HRESULT
msaa_acc_get_child_pos(IAccessible
*acc
, IAccessible
**out_parent
,
340 LONG child_count
, child_id
, child_pos
, match_pos
;
341 IAccessible
*child
, *parent
, *match
, **children
;
347 hr
= msaa_acc_get_parent(acc
, &parent
);
348 if (FAILED(hr
) || !parent
)
351 hr
= IAccessible_get_accChildCount(parent
, &child_count
);
352 if (FAILED(hr
) || !child_count
)
354 IAccessible_Release(parent
);
358 children
= heap_alloc_zero(sizeof(*children
) * child_count
);
360 return E_OUTOFMEMORY
;
363 for (i
= 0; i
< child_count
; i
++)
365 hr
= msaa_acc_get_next_child(parent
, i
+ 1, DIR_FORWARD
, &child
, &child_id
, &child_pos
, FALSE
);
366 if (FAILED(hr
) || !child
)
372 IAccessible_Release(child
);
375 for (i
= 0; i
< child_count
; i
++)
380 if (msaa_acc_compare(acc
, children
[i
]))
387 /* Can't have more than one IAccessible match. */
400 *out_parent
= parent
;
401 *out_child_pos
= match_pos
;
404 IAccessible_Release(parent
);
406 for (i
= 0; i
< child_count
; i
++)
409 IAccessible_Release(children
[i
]);
417 static LONG
msaa_role_to_uia_control_type(LONG role
)
421 case ROLE_SYSTEM_TITLEBAR
: return UIA_TitleBarControlTypeId
;
422 case ROLE_SYSTEM_MENUBAR
: return UIA_MenuBarControlTypeId
;
423 case ROLE_SYSTEM_SCROLLBAR
: return UIA_ScrollBarControlTypeId
;
424 case ROLE_SYSTEM_INDICATOR
:
425 case ROLE_SYSTEM_GRIP
: return UIA_ThumbControlTypeId
;
426 case ROLE_SYSTEM_APPLICATION
:
427 case ROLE_SYSTEM_WINDOW
: return UIA_WindowControlTypeId
;
428 case ROLE_SYSTEM_MENUPOPUP
: return UIA_MenuControlTypeId
;
429 case ROLE_SYSTEM_TOOLTIP
: return UIA_ToolTipControlTypeId
;
430 case ROLE_SYSTEM_DOCUMENT
: return UIA_DocumentControlTypeId
;
431 case ROLE_SYSTEM_PANE
: return UIA_PaneControlTypeId
;
432 case ROLE_SYSTEM_GROUPING
: return UIA_GroupControlTypeId
;
433 case ROLE_SYSTEM_SEPARATOR
: return UIA_SeparatorControlTypeId
;
434 case ROLE_SYSTEM_TOOLBAR
: return UIA_ToolBarControlTypeId
;
435 case ROLE_SYSTEM_STATUSBAR
: return UIA_StatusBarControlTypeId
;
436 case ROLE_SYSTEM_TABLE
: return UIA_TableControlTypeId
;
437 case ROLE_SYSTEM_COLUMNHEADER
:
438 case ROLE_SYSTEM_ROWHEADER
: return UIA_HeaderControlTypeId
;
439 case ROLE_SYSTEM_CELL
: return UIA_DataItemControlTypeId
;
440 case ROLE_SYSTEM_LINK
: return UIA_HyperlinkControlTypeId
;
441 case ROLE_SYSTEM_LIST
: return UIA_ListControlTypeId
;
442 case ROLE_SYSTEM_LISTITEM
: return UIA_ListItemControlTypeId
;
443 case ROLE_SYSTEM_OUTLINE
: return UIA_TreeControlTypeId
;
444 case ROLE_SYSTEM_OUTLINEITEM
: return UIA_TreeItemControlTypeId
;
445 case ROLE_SYSTEM_PAGETAB
: return UIA_TabItemControlTypeId
;
446 case ROLE_SYSTEM_GRAPHIC
: return UIA_ImageControlTypeId
;
447 case ROLE_SYSTEM_STATICTEXT
: return UIA_TextControlTypeId
;
448 case ROLE_SYSTEM_TEXT
: return UIA_EditControlTypeId
;
449 case ROLE_SYSTEM_CLOCK
:
450 case ROLE_SYSTEM_BUTTONDROPDOWNGRID
:
451 case ROLE_SYSTEM_PUSHBUTTON
: return UIA_ButtonControlTypeId
;
452 case ROLE_SYSTEM_CHECKBUTTON
: return UIA_CheckBoxControlTypeId
;
453 case ROLE_SYSTEM_RADIOBUTTON
: return UIA_RadioButtonControlTypeId
;
454 case ROLE_SYSTEM_COMBOBOX
: return UIA_ComboBoxControlTypeId
;
455 case ROLE_SYSTEM_PROGRESSBAR
: return UIA_ProgressBarControlTypeId
;
456 case ROLE_SYSTEM_SLIDER
: return UIA_SliderControlTypeId
;
457 case ROLE_SYSTEM_SPINBUTTON
: return UIA_SpinnerControlTypeId
;
458 case ROLE_SYSTEM_BUTTONMENU
:
459 case ROLE_SYSTEM_MENUITEM
: return UIA_MenuItemControlTypeId
;
460 case ROLE_SYSTEM_PAGETABLIST
: return UIA_TabControlTypeId
;
461 case ROLE_SYSTEM_BUTTONDROPDOWN
:
462 case ROLE_SYSTEM_SPLITBUTTON
: return UIA_SplitButtonControlTypeId
;
463 case ROLE_SYSTEM_SOUND
:
464 case ROLE_SYSTEM_CURSOR
:
465 case ROLE_SYSTEM_CARET
:
466 case ROLE_SYSTEM_ALERT
:
467 case ROLE_SYSTEM_CLIENT
:
468 case ROLE_SYSTEM_CHART
:
469 case ROLE_SYSTEM_DIALOG
:
470 case ROLE_SYSTEM_BORDER
:
471 case ROLE_SYSTEM_COLUMN
:
472 case ROLE_SYSTEM_ROW
:
473 case ROLE_SYSTEM_HELPBALLOON
:
474 case ROLE_SYSTEM_CHARACTER
:
475 case ROLE_SYSTEM_PROPERTYPAGE
:
476 case ROLE_SYSTEM_DROPLIST
:
477 case ROLE_SYSTEM_DIAL
:
478 case ROLE_SYSTEM_HOTKEYFIELD
:
479 case ROLE_SYSTEM_DIAGRAM
:
480 case ROLE_SYSTEM_ANIMATION
:
481 case ROLE_SYSTEM_EQUATION
:
482 case ROLE_SYSTEM_WHITESPACE
:
483 case ROLE_SYSTEM_IPADDRESS
:
484 case ROLE_SYSTEM_OUTLINEBUTTON
:
485 WARN("No UIA control type mapping for MSAA role %ld\n", role
);
489 FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role
);
497 * UiaProviderFromIAccessible IRawElementProviderSimple interface.
499 struct msaa_provider
{
500 IRawElementProviderSimple IRawElementProviderSimple_iface
;
501 IRawElementProviderFragment IRawElementProviderFragment_iface
;
502 ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface
;
511 BOOL root_acc_check_ran
;
518 static BOOL
msaa_check_root_acc(struct msaa_provider
*msaa_prov
)
523 if (msaa_prov
->root_acc_check_ran
)
524 return msaa_prov
->is_root_acc
;
526 msaa_prov
->root_acc_check_ran
= TRUE
;
527 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
|| msaa_prov
->parent
)
530 hr
= AccessibleObjectFromWindow(msaa_prov
->hwnd
, OBJID_CLIENT
, &IID_IAccessible
, (void **)&acc
);
534 if (msaa_acc_compare(msaa_prov
->acc
, acc
))
535 msaa_prov
->is_root_acc
= TRUE
;
537 IAccessible_Release(acc
);
538 return msaa_prov
->is_root_acc
;
541 static inline struct msaa_provider
*impl_from_msaa_provider(IRawElementProviderSimple
*iface
)
543 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderSimple_iface
);
546 HRESULT WINAPI
msaa_provider_QueryInterface(IRawElementProviderSimple
*iface
, REFIID riid
, void **ppv
)
548 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
551 if (IsEqualIID(riid
, &IID_IRawElementProviderSimple
) || IsEqualIID(riid
, &IID_IUnknown
))
553 else if (IsEqualIID(riid
, &IID_IRawElementProviderFragment
))
554 *ppv
= &msaa_prov
->IRawElementProviderFragment_iface
;
555 else if (IsEqualIID(riid
, &IID_ILegacyIAccessibleProvider
))
556 *ppv
= &msaa_prov
->ILegacyIAccessibleProvider_iface
;
558 return E_NOINTERFACE
;
560 IRawElementProviderSimple_AddRef(iface
);
564 ULONG WINAPI
msaa_provider_AddRef(IRawElementProviderSimple
*iface
)
566 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
567 ULONG refcount
= InterlockedIncrement(&msaa_prov
->refcount
);
569 TRACE("%p, refcount %ld\n", iface
, refcount
);
574 ULONG WINAPI
msaa_provider_Release(IRawElementProviderSimple
*iface
)
576 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
577 ULONG refcount
= InterlockedDecrement(&msaa_prov
->refcount
);
579 TRACE("%p, refcount %ld\n", iface
, refcount
);
583 IAccessible_Release(msaa_prov
->acc
);
584 if (msaa_prov
->parent
)
585 IAccessible_Release(msaa_prov
->parent
);
587 IAccessible2_Release(msaa_prov
->ia2
);
588 heap_free(msaa_prov
);
594 HRESULT WINAPI
msaa_provider_get_ProviderOptions(IRawElementProviderSimple
*iface
,
595 enum ProviderOptions
*ret_val
)
597 TRACE("%p, %p\n", iface
, ret_val
);
598 *ret_val
= ProviderOptions_ServerSideProvider
| ProviderOptions_UseComThreading
;
602 HRESULT WINAPI
msaa_provider_GetPatternProvider(IRawElementProviderSimple
*iface
,
603 PATTERNID pattern_id
, IUnknown
**ret_val
)
605 TRACE("%p, %d, %p\n", iface
, pattern_id
, ret_val
);
610 case UIA_LegacyIAccessiblePatternId
:
611 return IRawElementProviderSimple_QueryInterface(iface
, &IID_IUnknown
, (void **)ret_val
);
614 FIXME("Unimplemented patternId %d\n", pattern_id
);
621 HRESULT WINAPI
msaa_provider_GetPropertyValue(IRawElementProviderSimple
*iface
,
622 PROPERTYID prop_id
, VARIANT
*ret_val
)
624 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
628 TRACE("%p, %d, %p\n", iface
, prop_id
, ret_val
);
630 VariantInit(ret_val
);
634 case UIA_ProviderDescriptionPropertyId
:
635 V_VT(ret_val
) = VT_BSTR
;
636 V_BSTR(ret_val
) = SysAllocString(L
"Wine: MSAA Proxy");
639 case UIA_ControlTypePropertyId
:
640 if (!msaa_prov
->control_type
)
642 hr
= IAccessible_get_accRole(msaa_prov
->acc
, msaa_prov
->cid
, &v
);
643 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
644 msaa_prov
->control_type
= msaa_role_to_uia_control_type(V_I4(&v
));
647 if (msaa_prov
->control_type
)
648 variant_init_i4(ret_val
, msaa_prov
->control_type
);
652 case UIA_HasKeyboardFocusPropertyId
:
653 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
654 STATE_SYSTEM_FOCUSED
));
657 case UIA_IsKeyboardFocusablePropertyId
:
658 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
659 STATE_SYSTEM_FOCUSABLE
));
662 case UIA_IsEnabledPropertyId
:
663 variant_init_bool(ret_val
, !msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
664 STATE_SYSTEM_UNAVAILABLE
));
667 case UIA_IsPasswordPropertyId
:
668 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
669 STATE_SYSTEM_PROTECTED
));
673 FIXME("Unimplemented propertyId %d\n", prop_id
);
680 HRESULT WINAPI
msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple
*iface
,
681 IRawElementProviderSimple
**ret_val
)
683 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
685 TRACE("%p, %p\n", iface
, ret_val
);
688 if (msaa_check_root_acc(msaa_prov
))
689 return UiaHostProviderFromHwnd(msaa_prov
->hwnd
, ret_val
);
694 static const IRawElementProviderSimpleVtbl msaa_provider_vtbl
= {
695 msaa_provider_QueryInterface
,
696 msaa_provider_AddRef
,
697 msaa_provider_Release
,
698 msaa_provider_get_ProviderOptions
,
699 msaa_provider_GetPatternProvider
,
700 msaa_provider_GetPropertyValue
,
701 msaa_provider_get_HostRawElementProvider
,
705 * IRawElementProviderFragment interface for UiaProviderFromIAccessible
708 static inline struct msaa_provider
*impl_from_msaa_fragment(IRawElementProviderFragment
*iface
)
710 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderFragment_iface
);
713 static HRESULT WINAPI
msaa_fragment_QueryInterface(IRawElementProviderFragment
*iface
, REFIID riid
,
716 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
717 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
720 static ULONG WINAPI
msaa_fragment_AddRef(IRawElementProviderFragment
*iface
)
722 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
723 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
726 static ULONG WINAPI
msaa_fragment_Release(IRawElementProviderFragment
*iface
)
728 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
729 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
732 static HRESULT WINAPI
msaa_fragment_Navigate(IRawElementProviderFragment
*iface
,
733 enum NavigateDirection direction
, IRawElementProviderFragment
**ret_val
)
735 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
736 LONG child_count
, child_id
, child_pos
;
737 IRawElementProviderSimple
*elprov
;
741 TRACE("%p, %d, %p\n", iface
, direction
, ret_val
);
746 case NavigateDirection_Parent
:
747 if (msaa_check_root_acc(msaa_prov
))
750 if (V_I4(&msaa_prov
->cid
) == CHILDID_SELF
)
752 hr
= msaa_acc_get_parent(msaa_prov
->acc
, &acc
);
753 if (FAILED(hr
) || !acc
)
757 acc
= msaa_prov
->acc
;
759 hr
= UiaProviderFromIAccessible(acc
, CHILDID_SELF
, 0, &elprov
);
762 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
763 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
766 if (acc
!= msaa_prov
->acc
)
767 IAccessible_Release(acc
);
771 case NavigateDirection_FirstChild
:
772 case NavigateDirection_LastChild
:
773 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
776 hr
= IAccessible_get_accChildCount(msaa_prov
->acc
, &child_count
);
777 if (FAILED(hr
) || !child_count
)
780 if (direction
== NavigateDirection_FirstChild
)
781 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, 1, DIR_FORWARD
, &acc
, &child_id
,
784 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, child_count
, DIR_REVERSE
, &acc
, &child_id
,
787 if (FAILED(hr
) || !acc
)
790 hr
= UiaProviderFromIAccessible(acc
, child_id
, 0, &elprov
);
793 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
795 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
796 prov
->parent
= msaa_prov
->acc
;
797 IAccessible_AddRef(msaa_prov
->acc
);
798 if (acc
!= msaa_prov
->acc
)
799 prov
->child_pos
= child_pos
;
801 prov
->child_pos
= child_id
;
803 IAccessible_Release(acc
);
807 case NavigateDirection_NextSibling
:
808 case NavigateDirection_PreviousSibling
:
809 if (msaa_check_root_acc(msaa_prov
))
812 if (!msaa_prov
->parent
)
814 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
816 msaa_prov
->parent
= msaa_prov
->acc
;
817 IAccessible_AddRef(msaa_prov
->acc
);
818 msaa_prov
->child_pos
= V_I4(&msaa_prov
->cid
);
822 hr
= msaa_acc_get_child_pos(msaa_prov
->acc
, &acc
, &child_pos
);
823 if (FAILED(hr
) || !acc
)
825 msaa_prov
->parent
= acc
;
826 msaa_prov
->child_pos
= child_pos
;
830 if (direction
== NavigateDirection_NextSibling
)
831 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
+ 1, DIR_FORWARD
,
832 &acc
, &child_id
, &child_pos
, TRUE
);
834 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
- 1, DIR_REVERSE
,
835 &acc
, &child_id
, &child_pos
, TRUE
);
837 if (FAILED(hr
) || !acc
)
840 hr
= UiaProviderFromIAccessible(acc
, child_id
, 0, &elprov
);
843 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
845 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
846 prov
->parent
= msaa_prov
->parent
;
847 IAccessible_AddRef(msaa_prov
->parent
);
848 if (acc
!= msaa_prov
->acc
)
849 prov
->child_pos
= child_pos
;
851 prov
->child_pos
= child_id
;
853 IAccessible_Release(acc
);
858 FIXME("Invalid NavigateDirection %d\n", direction
);
865 static HRESULT WINAPI
msaa_fragment_GetRuntimeId(IRawElementProviderFragment
*iface
,
868 FIXME("%p, %p: stub!\n", iface
, ret_val
);
873 static HRESULT WINAPI
msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment
*iface
,
874 struct UiaRect
*ret_val
)
876 FIXME("%p, %p: stub!\n", iface
, ret_val
);
880 static HRESULT WINAPI
msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment
*iface
,
883 FIXME("%p, %p: stub!\n", iface
, ret_val
);
888 static HRESULT WINAPI
msaa_fragment_SetFocus(IRawElementProviderFragment
*iface
)
890 FIXME("%p: stub!\n", iface
);
894 static HRESULT WINAPI
msaa_fragment_get_FragmentRoot(IRawElementProviderFragment
*iface
,
895 IRawElementProviderFragmentRoot
**ret_val
)
897 FIXME("%p, %p: stub!\n", iface
, ret_val
);
902 static const IRawElementProviderFragmentVtbl msaa_fragment_vtbl
= {
903 msaa_fragment_QueryInterface
,
904 msaa_fragment_AddRef
,
905 msaa_fragment_Release
,
906 msaa_fragment_Navigate
,
907 msaa_fragment_GetRuntimeId
,
908 msaa_fragment_get_BoundingRectangle
,
909 msaa_fragment_GetEmbeddedFragmentRoots
,
910 msaa_fragment_SetFocus
,
911 msaa_fragment_get_FragmentRoot
,
915 * ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible
918 static inline struct msaa_provider
*impl_from_msaa_acc_provider(ILegacyIAccessibleProvider
*iface
)
920 return CONTAINING_RECORD(iface
, struct msaa_provider
, ILegacyIAccessibleProvider_iface
);
923 static HRESULT WINAPI
msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider
*iface
, REFIID riid
, void **ppv
)
925 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
926 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
929 static ULONG WINAPI
msaa_acc_provider_AddRef(ILegacyIAccessibleProvider
*iface
)
931 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
932 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
935 static ULONG WINAPI
msaa_acc_provider_Release(ILegacyIAccessibleProvider
*iface
)
937 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
938 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
941 static HRESULT WINAPI
msaa_acc_provider_Select(ILegacyIAccessibleProvider
*iface
, LONG select_flags
)
943 FIXME("%p, %#lx: stub!\n", iface
, select_flags
);
947 static HRESULT WINAPI
msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider
*iface
)
949 FIXME("%p: stub!\n", iface
);
953 static HRESULT WINAPI
msaa_acc_provider_SetValue(ILegacyIAccessibleProvider
*iface
, LPCWSTR val
)
955 FIXME("%p, %p<%s>: stub!\n", iface
, val
, debugstr_w(val
));
959 static HRESULT WINAPI
msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider
*iface
,
960 IAccessible
**out_acc
)
962 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
964 TRACE("%p, %p\n", iface
, out_acc
);
966 IAccessible_AddRef(msaa_prov
->acc
);
967 *out_acc
= msaa_prov
->acc
;
972 static HRESULT WINAPI
msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider
*iface
, int *out_cid
)
974 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
976 TRACE("%p, %p\n", iface
, out_cid
);
977 *out_cid
= V_I4(&msaa_prov
->cid
);
982 static HRESULT WINAPI
msaa_acc_provider_get_Name(ILegacyIAccessibleProvider
*iface
, BSTR
*out_name
)
984 FIXME("%p, %p: stub!\n", iface
, out_name
);
988 static HRESULT WINAPI
msaa_acc_provider_get_Value(ILegacyIAccessibleProvider
*iface
, BSTR
*out_value
)
990 FIXME("%p, %p: stub!\n", iface
, out_value
);
994 static HRESULT WINAPI
msaa_acc_provider_get_Description(ILegacyIAccessibleProvider
*iface
,
995 BSTR
*out_description
)
997 FIXME("%p, %p: stub!\n", iface
, out_description
);
1001 static HRESULT WINAPI
msaa_acc_provider_get_Role(ILegacyIAccessibleProvider
*iface
, DWORD
*out_role
)
1003 FIXME("%p, %p: stub!\n", iface
, out_role
);
1007 static HRESULT WINAPI
msaa_acc_provider_get_State(ILegacyIAccessibleProvider
*iface
, DWORD
*out_state
)
1009 FIXME("%p, %p: stub!\n", iface
, out_state
);
1013 static HRESULT WINAPI
msaa_acc_provider_get_Help(ILegacyIAccessibleProvider
*iface
, BSTR
*out_help
)
1015 FIXME("%p, %p: stub!\n", iface
, out_help
);
1019 static HRESULT WINAPI
msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider
*iface
,
1020 BSTR
*out_kbd_shortcut
)
1022 FIXME("%p, %p: stub!\n", iface
, out_kbd_shortcut
);
1026 static HRESULT WINAPI
msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider
*iface
,
1027 SAFEARRAY
**out_selected
)
1029 FIXME("%p, %p: stub!\n", iface
, out_selected
);
1033 static HRESULT WINAPI
msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider
*iface
,
1034 BSTR
*out_default_action
)
1036 FIXME("%p, %p: stub!\n", iface
, out_default_action
);
1040 static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl
= {
1041 msaa_acc_provider_QueryInterface
,
1042 msaa_acc_provider_AddRef
,
1043 msaa_acc_provider_Release
,
1044 msaa_acc_provider_Select
,
1045 msaa_acc_provider_DoDefaultAction
,
1046 msaa_acc_provider_SetValue
,
1047 msaa_acc_provider_GetIAccessible
,
1048 msaa_acc_provider_get_ChildId
,
1049 msaa_acc_provider_get_Name
,
1050 msaa_acc_provider_get_Value
,
1051 msaa_acc_provider_get_Description
,
1052 msaa_acc_provider_get_Role
,
1053 msaa_acc_provider_get_State
,
1054 msaa_acc_provider_get_Help
,
1055 msaa_acc_provider_get_KeyboardShortcut
,
1056 msaa_acc_provider_GetSelection
,
1057 msaa_acc_provider_get_DefaultAction
,
1060 /***********************************************************************
1061 * UiaProviderFromIAccessible (uiautomationcore.@)
1063 HRESULT WINAPI
UiaProviderFromIAccessible(IAccessible
*acc
, long child_id
, DWORD flags
,
1064 IRawElementProviderSimple
**elprov
)
1066 struct msaa_provider
*msaa_prov
;
1067 IServiceProvider
*serv_prov
;
1071 TRACE("(%p, %ld, %#lx, %p)\n", acc
, child_id
, flags
, elprov
);
1079 return E_INVALIDARG
;
1081 if (flags
!= UIA_PFIA_DEFAULT
)
1083 FIXME("unsupported flags %#lx\n", flags
);
1087 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
1092 hr
= IServiceProvider_QueryService(serv_prov
, &IIS_IsOleaccProxy
, &IID_IUnknown
, (void **)&unk
);
1095 WARN("Cannot wrap an oleacc proxy IAccessible!\n");
1096 IUnknown_Release(unk
);
1097 IServiceProvider_Release(serv_prov
);
1098 return E_INVALIDARG
;
1101 IServiceProvider_Release(serv_prov
);
1104 hr
= WindowFromAccessibleObject(acc
, &hwnd
);
1110 msaa_prov
= heap_alloc_zero(sizeof(*msaa_prov
));
1112 return E_OUTOFMEMORY
;
1114 msaa_prov
->IRawElementProviderSimple_iface
.lpVtbl
= &msaa_provider_vtbl
;
1115 msaa_prov
->IRawElementProviderFragment_iface
.lpVtbl
= &msaa_fragment_vtbl
;
1116 msaa_prov
->ILegacyIAccessibleProvider_iface
.lpVtbl
= &msaa_acc_provider_vtbl
;
1117 msaa_prov
->refcount
= 1;
1118 msaa_prov
->hwnd
= hwnd
;
1119 variant_init_i4(&msaa_prov
->cid
, child_id
);
1120 msaa_prov
->acc
= acc
;
1121 IAccessible_AddRef(acc
);
1122 msaa_prov
->ia2
= msaa_acc_get_ia2(acc
);
1123 *elprov
= &msaa_prov
->IRawElementProviderSimple_iface
;
1129 * UI Automation provider thread functions.
1131 struct uia_provider_thread
1133 struct list nodes_list
;
1139 static struct uia_provider_thread provider_thread
;
1140 static CRITICAL_SECTION provider_thread_cs
;
1141 static CRITICAL_SECTION_DEBUG provider_thread_cs_debug
=
1143 0, 0, &provider_thread_cs
,
1144 { &provider_thread_cs_debug
.ProcessLocksList
, &provider_thread_cs_debug
.ProcessLocksList
},
1145 0, 0, { (DWORD_PTR
)(__FILE__
": provider_thread_cs") }
1147 static CRITICAL_SECTION provider_thread_cs
= { &provider_thread_cs_debug
, -1, 0, 0, 0, 0 };
1149 void uia_provider_thread_remove_node(HUIANODE node
)
1151 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1153 TRACE("Removing node %p\n", node
);
1155 EnterCriticalSection(&provider_thread_cs
);
1156 list_remove(&node_data
->prov_thread_list_entry
);
1157 list_init(&node_data
->prov_thread_list_entry
);
1158 SafeArrayDestroy(node_data
->runtime_id
);
1159 node_data
->runtime_id
= NULL
;
1160 LeaveCriticalSection(&provider_thread_cs
);
1163 static void uia_provider_thread_disconnect_node(SAFEARRAY
*sa
)
1165 struct list
*cursor
, *cursor2
;
1166 struct uia_node
*node_data
;
1168 EnterCriticalSection(&provider_thread_cs
);
1170 /* Provider thread hasn't been started, no nodes to disconnect. */
1171 if (!provider_thread
.ref
)
1174 LIST_FOR_EACH_SAFE(cursor
, cursor2
, &provider_thread
.nodes_list
)
1176 node_data
= LIST_ENTRY(cursor
, struct uia_node
, prov_thread_list_entry
);
1178 if (node_data
->runtime_id
)
1180 if (!uia_compare_runtime_ids(sa
, node_data
->runtime_id
))
1182 list_remove(cursor
);
1183 list_init(&node_data
->prov_thread_list_entry
);
1184 IWineUiaNode_disconnect(&node_data
->IWineUiaNode_iface
);
1185 SafeArrayDestroy(node_data
->runtime_id
);
1186 node_data
->runtime_id
= NULL
;
1192 LeaveCriticalSection(&provider_thread_cs
);
1195 static HRESULT
uia_provider_thread_add_node(HUIANODE node
)
1197 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1201 node_data
->nested_node
= TRUE
;
1202 hr
= UiaGetRuntimeId(node
, &sa
);
1206 TRACE("Adding node %p\n", node
);
1208 EnterCriticalSection(&provider_thread_cs
);
1209 node_data
->runtime_id
= sa
;
1210 list_add_tail(&provider_thread
.nodes_list
, &node_data
->prov_thread_list_entry
);
1211 LeaveCriticalSection(&provider_thread_cs
);
1216 #define WM_GET_OBJECT_UIA_NODE (WM_USER + 1)
1217 #define WM_UIA_PROVIDER_THREAD_STOP (WM_USER + 2)
1218 static LRESULT CALLBACK
uia_provider_thread_msg_proc(HWND hwnd
, UINT msg
, WPARAM wparam
,
1223 case WM_GET_OBJECT_UIA_NODE
:
1225 HUIANODE node
= (HUIANODE
)lparam
;
1228 if (FAILED(uia_provider_thread_add_node(node
)))
1230 WARN("Failed to add node %p to provider thread list.\n", node
);
1231 UiaNodeRelease(node
);
1236 * LresultFromObject returns an index into the global atom string table,
1237 * which has a valid range of 0xc000-0xffff.
1239 lr
= LresultFromObject(&IID_IWineUiaNode
, 0, (IUnknown
*)node
);
1240 if ((lr
> 0xffff) || (lr
< 0xc000))
1242 WARN("Got invalid lresult %Ix\n", lr
);
1247 * LresultFromObject increases refcnt by 1. If LresultFromObject
1248 * failed, this is expected to release the node.
1250 UiaNodeRelease(node
);
1258 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
1261 static DWORD WINAPI
uia_provider_thread_proc(void *arg
)
1263 HANDLE initialized_event
= arg
;
1267 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
1268 hwnd
= CreateWindowW(L
"Message", NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, NULL
, NULL
, NULL
);
1271 WARN("CreateWindow failed: %ld\n", GetLastError());
1273 FreeLibraryAndExitThread(huia_module
, 1);
1276 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (LONG_PTR
)uia_provider_thread_msg_proc
);
1277 provider_thread
.hwnd
= hwnd
;
1279 /* Initialization complete, thread can now process window messages. */
1280 SetEvent(initialized_event
);
1281 TRACE("Provider thread started.\n");
1282 while (GetMessageW(&msg
, NULL
, 0, 0))
1284 if (msg
.message
== WM_UIA_PROVIDER_THREAD_STOP
)
1286 TranslateMessage(&msg
);
1287 DispatchMessageW(&msg
);
1290 TRACE("Shutting down UI Automation provider thread.\n");
1292 DestroyWindow(hwnd
);
1294 FreeLibraryAndExitThread(huia_module
, 0);
1297 static BOOL
uia_start_provider_thread(void)
1299 BOOL started
= TRUE
;
1301 EnterCriticalSection(&provider_thread_cs
);
1302 if (++provider_thread
.ref
== 1)
1309 /* Increment DLL reference count. */
1310 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
1311 (const WCHAR
*)uia_start_provider_thread
, &hmodule
);
1313 list_init(&provider_thread
.nodes_list
);
1314 events
[0] = ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1315 if (!(provider_thread
.hthread
= CreateThread(NULL
, 0, uia_provider_thread_proc
,
1316 ready_event
, 0, NULL
)))
1318 FreeLibrary(hmodule
);
1323 events
[1] = provider_thread
.hthread
;
1324 wait_obj
= WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
1325 if (wait_obj
!= WAIT_OBJECT_0
)
1327 CloseHandle(provider_thread
.hthread
);
1332 CloseHandle(ready_event
);
1335 WARN("Failed to start provider thread\n");
1336 memset(&provider_thread
, 0, sizeof(provider_thread
));
1340 LeaveCriticalSection(&provider_thread_cs
);
1344 void uia_stop_provider_thread(void)
1346 EnterCriticalSection(&provider_thread_cs
);
1347 if (!--provider_thread
.ref
)
1349 PostMessageW(provider_thread
.hwnd
, WM_UIA_PROVIDER_THREAD_STOP
, 0, 0);
1350 CloseHandle(provider_thread
.hthread
);
1351 if (!list_empty(&provider_thread
.nodes_list
))
1352 ERR("Provider thread shutdown with nodes still in the list\n");
1353 memset(&provider_thread
, 0, sizeof(provider_thread
));
1355 LeaveCriticalSection(&provider_thread_cs
);
1359 * Pass our IWineUiaNode interface to the provider thread for marshaling. UI
1360 * Automation has to work regardless of whether or not COM is initialized on
1361 * the thread calling UiaReturnRawElementProvider.
1363 static LRESULT
uia_lresult_from_node(HUIANODE huianode
)
1365 if (!uia_start_provider_thread())
1367 UiaNodeRelease(huianode
);
1371 return SendMessageW(provider_thread
.hwnd
, WM_GET_OBJECT_UIA_NODE
, 0, (LPARAM
)huianode
);
1374 /***********************************************************************
1375 * UiaReturnRawElementProvider (uiautomationcore.@)
1377 LRESULT WINAPI
UiaReturnRawElementProvider(HWND hwnd
, WPARAM wparam
,
1378 LPARAM lparam
, IRawElementProviderSimple
*elprov
)
1383 TRACE("(%p, %Ix, %#Ix, %p)\n", hwnd
, wparam
, lparam
, elprov
);
1385 if (!wparam
&& !lparam
&& !elprov
)
1387 FIXME("UIA-to-MSAA bridge not implemented, no provider map to free.\n");
1391 if (lparam
!= UiaRootObjectId
)
1393 FIXME("Unsupported object id %Id, ignoring.\n", lparam
);
1397 hr
= UiaNodeFromProvider(elprov
, &node
);
1400 WARN("Failed to create HUIANODE with hr %#lx\n", hr
);
1404 return uia_lresult_from_node(node
);
1407 /***********************************************************************
1408 * UiaDisconnectProvider (uiautomationcore.@)
1410 HRESULT WINAPI
UiaDisconnectProvider(IRawElementProviderSimple
*elprov
)
1416 TRACE("(%p)\n", elprov
);
1418 hr
= UiaNodeFromProvider(elprov
, &node
);
1422 hr
= UiaGetRuntimeId(node
, &sa
);
1423 UiaNodeRelease(node
);
1428 return E_INVALIDARG
;
1430 uia_provider_thread_disconnect_node(sa
);
1432 SafeArrayDestroy(sa
);