dmband: Rewrite band lbin list parsing.
[wine.git] / dlls / uiautomationcore / uia_provider.c
blobb16077b607c9b300b51198dccc5ad898948c68e0
1 /*
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"
20 #include "ocidl.h"
22 #include "wine/debug.h"
23 #include "initguid.h"
24 #include "wine/iaccessible2.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
28 DEFINE_GUID(SID_AccFromDAWrapper, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
30 static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, ULONG flag)
32 HRESULT hr;
33 VARIANT v;
35 VariantInit(&v);
36 hr = IAccessible_get_accState(acc, cid, &v);
37 if (SUCCEEDED(hr) && V_VT(&v) == VT_I4 && (V_I4(&v) & flag))
38 return TRUE;
40 return FALSE;
43 static IAccessible2 *msaa_acc_get_ia2(IAccessible *acc)
45 IServiceProvider *serv_prov;
46 IAccessible2 *ia2 = NULL;
47 HRESULT hr;
49 hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
50 if (SUCCEEDED(hr))
52 hr = IServiceProvider_QueryService(serv_prov, &IID_IAccessible2, &IID_IAccessible2, (void **)&ia2);
53 IServiceProvider_Release(serv_prov);
54 if (SUCCEEDED(hr) && ia2)
55 return ia2;
58 hr = IAccessible_QueryInterface(acc, &IID_IAccessible2, (void **)&ia2);
59 if (SUCCEEDED(hr) && ia2)
60 return ia2;
62 return NULL;
65 static IAccessible *msaa_acc_da_unwrap(IAccessible *acc)
67 IServiceProvider *sp;
68 IAccessible *acc2;
69 HRESULT hr;
71 hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void**)&sp);
72 if (SUCCEEDED(hr))
74 hr = IServiceProvider_QueryService(sp, &SID_AccFromDAWrapper, &IID_IAccessible, (void**)&acc2);
75 IServiceProvider_Release(sp);
78 if (SUCCEEDED(hr) && acc2)
79 return acc2;
81 IAccessible_AddRef(acc);
82 return acc;
86 * Compare role, state, child count, and location properties of the two
87 * IAccessibles. If all four are successfully retrieved and are equal, this is
88 * considered a match.
90 static HRESULT msaa_acc_prop_match(IAccessible *acc, IAccessible *acc2)
92 BOOL role_match, state_match, child_count_match, location_match;
93 LONG child_count[2], left[2], top[2], width[2], height[2];
94 VARIANT cid, v, v2;
95 HRESULT hr, hr2;
97 role_match = state_match = child_count_match = location_match = FALSE;
98 variant_init_i4(&cid, CHILDID_SELF);
99 hr = IAccessible_get_accRole(acc, cid, &v);
100 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
102 VariantInit(&v2);
103 hr = IAccessible_get_accRole(acc2, cid, &v2);
104 if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
106 if (V_I4(&v) != V_I4(&v2))
107 return E_FAIL;
109 role_match = TRUE;
113 VariantInit(&v);
114 hr = IAccessible_get_accState(acc, cid, &v);
115 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
117 VariantInit(&v2);
118 hr = IAccessible_get_accState(acc2, cid, &v2);
119 if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
121 if (V_I4(&v) != V_I4(&v2))
122 return E_FAIL;
124 state_match = TRUE;
128 hr = IAccessible_get_accChildCount(acc, &child_count[0]);
129 hr2 = IAccessible_get_accChildCount(acc2, &child_count[1]);
130 if (SUCCEEDED(hr) && SUCCEEDED(hr2))
132 if (child_count[0] != child_count[1])
133 return E_FAIL;
135 child_count_match = TRUE;
138 hr = IAccessible_accLocation(acc, &left[0], &top[0], &width[0], &height[0], cid);
139 if (SUCCEEDED(hr))
141 hr = IAccessible_accLocation(acc2, &left[1], &top[1], &width[1], &height[1], cid);
142 if (SUCCEEDED(hr))
144 if ((left[0] != left[1]) || (top[0] != top[1]) || (width[0] != width[1]) ||
145 (height[0] != height[1]))
146 return E_FAIL;
148 location_match = TRUE;
152 if (role_match && state_match && child_count_match && location_match)
153 return S_OK;
155 return S_FALSE;
158 static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2)
160 IAccessible2 *ia2[2] = { NULL, NULL };
161 IUnknown *unk, *unk2;
162 BOOL matched = FALSE;
163 LONG unique_id[2];
164 BSTR name[2];
165 VARIANT cid;
166 HRESULT hr;
168 acc = msaa_acc_da_unwrap(acc);
169 acc2 = msaa_acc_da_unwrap(acc2);
170 IAccessible_QueryInterface(acc, &IID_IUnknown, (void**)&unk);
171 IAccessible_QueryInterface(acc2, &IID_IUnknown, (void**)&unk2);
172 if (unk == unk2)
174 matched = TRUE;
175 goto exit;
178 ia2[0] = msaa_acc_get_ia2(acc);
179 ia2[1] = msaa_acc_get_ia2(acc2);
180 if (!ia2[0] != !ia2[1])
181 goto exit;
182 if (ia2[0])
184 hr = IAccessible2_get_uniqueID(ia2[0], &unique_id[0]);
185 if (SUCCEEDED(hr))
187 hr = IAccessible2_get_uniqueID(ia2[1], &unique_id[1]);
188 if (SUCCEEDED(hr))
190 if (unique_id[0] == unique_id[1])
191 matched = TRUE;
193 goto exit;
198 hr = msaa_acc_prop_match(acc, acc2);
199 if (FAILED(hr))
200 goto exit;
201 if (hr == S_OK)
202 matched = TRUE;
204 variant_init_i4(&cid, CHILDID_SELF);
205 hr = IAccessible_get_accName(acc, cid, &name[0]);
206 if (SUCCEEDED(hr))
208 hr = IAccessible_get_accName(acc2, cid, &name[1]);
209 if (SUCCEEDED(hr))
211 if (!name[0] && !name[1])
212 matched = TRUE;
213 else if (!name[0] || !name[1])
214 matched = FALSE;
215 else
217 if (!wcscmp(name[0], name[1]))
218 matched = TRUE;
219 else
220 matched = FALSE;
223 SysFreeString(name[1]);
226 SysFreeString(name[0]);
229 exit:
230 IUnknown_Release(unk);
231 IUnknown_Release(unk2);
232 IAccessible_Release(acc);
233 IAccessible_Release(acc2);
234 if (ia2[0])
235 IAccessible2_Release(ia2[0]);
236 if (ia2[1])
237 IAccessible2_Release(ia2[1]);
239 return matched;
242 static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
244 IDispatch *disp = NULL;
245 HRESULT hr;
247 *parent = NULL;
248 hr = IAccessible_get_accParent(acc, &disp);
249 if (FAILED(hr) || !disp)
250 return hr;
252 hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void**)parent);
253 IDispatch_Release(disp);
254 return hr;
257 #define DIR_FORWARD 0
258 #define DIR_REVERSE 1
259 static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG direction,
260 IAccessible **child, LONG *child_id, LONG *child_pos, BOOL check_visible)
262 LONG child_count, cur_pos;
263 IDispatch *disp;
264 VARIANT cid;
265 HRESULT hr;
267 *child = NULL;
268 *child_id = 0;
269 cur_pos = start_pos;
270 while (1)
272 hr = IAccessible_get_accChildCount(acc, &child_count);
273 if (FAILED(hr) || (cur_pos > child_count))
274 break;
276 variant_init_i4(&cid, cur_pos);
277 hr = IAccessible_get_accChild(acc, cid, &disp);
278 if (FAILED(hr))
279 break;
281 if (hr == S_FALSE)
283 if (!check_visible || !msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
285 *child = acc;
286 *child_id = *child_pos = cur_pos;
287 IAccessible_AddRef(acc);
288 return S_OK;
291 else
293 IAccessible *acc_child = NULL;
295 hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc_child);
296 IDispatch_Release(disp);
297 if (FAILED(hr))
298 break;
300 variant_init_i4(&cid, CHILDID_SELF);
301 if (!check_visible || !msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
303 *child = acc_child;
304 *child_id = CHILDID_SELF;
305 *child_pos = cur_pos;
306 return S_OK;
309 IAccessible_Release(acc_child);
312 if (direction == DIR_FORWARD)
313 cur_pos++;
314 else
315 cur_pos--;
317 if ((cur_pos > child_count) || (cur_pos <= 0))
318 break;
321 return hr;
324 static HRESULT msaa_acc_get_child_pos(IAccessible *acc, IAccessible **out_parent,
325 LONG *out_child_pos)
327 LONG child_count, child_id, child_pos, match_pos;
328 IAccessible *child, *parent, *match, **children;
329 HRESULT hr;
330 int i;
332 *out_parent = NULL;
333 *out_child_pos = 0;
334 hr = msaa_acc_get_parent(acc, &parent);
335 if (FAILED(hr) || !parent)
336 return hr;
338 hr = IAccessible_get_accChildCount(parent, &child_count);
339 if (FAILED(hr) || !child_count)
341 IAccessible_Release(parent);
342 return hr;
345 children = calloc(child_count, sizeof(*children));
346 if (!children)
347 return E_OUTOFMEMORY;
349 match = NULL;
350 for (i = 0; i < child_count; i++)
352 hr = msaa_acc_get_next_child(parent, i + 1, DIR_FORWARD, &child, &child_id, &child_pos, FALSE);
353 if (FAILED(hr) || !child)
354 goto exit;
356 if (child != parent)
357 children[i] = child;
358 else
359 IAccessible_Release(child);
362 for (i = 0; i < child_count; i++)
364 if (!children[i])
365 continue;
367 if (msaa_acc_compare(acc, children[i]))
369 if (!match)
371 match = children[i];
372 match_pos = i + 1;
374 /* Can't have more than one IAccessible match. */
375 else
377 match = NULL;
378 match_pos = 0;
379 break;
384 exit:
385 if (match)
387 *out_parent = parent;
388 *out_child_pos = match_pos;
390 else
391 IAccessible_Release(parent);
393 for (i = 0; i < child_count; i++)
395 if (children[i])
396 IAccessible_Release(children[i]);
399 free(children);
401 return hr;
404 static LONG msaa_role_to_uia_control_type(LONG role)
406 switch (role)
408 case ROLE_SYSTEM_TITLEBAR: return UIA_TitleBarControlTypeId;
409 case ROLE_SYSTEM_MENUBAR: return UIA_MenuBarControlTypeId;
410 case ROLE_SYSTEM_SCROLLBAR: return UIA_ScrollBarControlTypeId;
411 case ROLE_SYSTEM_INDICATOR:
412 case ROLE_SYSTEM_GRIP: return UIA_ThumbControlTypeId;
413 case ROLE_SYSTEM_APPLICATION:
414 case ROLE_SYSTEM_WINDOW: return UIA_WindowControlTypeId;
415 case ROLE_SYSTEM_MENUPOPUP: return UIA_MenuControlTypeId;
416 case ROLE_SYSTEM_TOOLTIP: return UIA_ToolTipControlTypeId;
417 case ROLE_SYSTEM_DOCUMENT: return UIA_DocumentControlTypeId;
418 case ROLE_SYSTEM_PANE: return UIA_PaneControlTypeId;
419 case ROLE_SYSTEM_GROUPING: return UIA_GroupControlTypeId;
420 case ROLE_SYSTEM_SEPARATOR: return UIA_SeparatorControlTypeId;
421 case ROLE_SYSTEM_TOOLBAR: return UIA_ToolBarControlTypeId;
422 case ROLE_SYSTEM_STATUSBAR: return UIA_StatusBarControlTypeId;
423 case ROLE_SYSTEM_TABLE: return UIA_TableControlTypeId;
424 case ROLE_SYSTEM_COLUMNHEADER:
425 case ROLE_SYSTEM_ROWHEADER: return UIA_HeaderControlTypeId;
426 case ROLE_SYSTEM_CELL: return UIA_DataItemControlTypeId;
427 case ROLE_SYSTEM_LINK: return UIA_HyperlinkControlTypeId;
428 case ROLE_SYSTEM_LIST: return UIA_ListControlTypeId;
429 case ROLE_SYSTEM_LISTITEM: return UIA_ListItemControlTypeId;
430 case ROLE_SYSTEM_OUTLINE: return UIA_TreeControlTypeId;
431 case ROLE_SYSTEM_OUTLINEITEM: return UIA_TreeItemControlTypeId;
432 case ROLE_SYSTEM_PAGETAB: return UIA_TabItemControlTypeId;
433 case ROLE_SYSTEM_GRAPHIC: return UIA_ImageControlTypeId;
434 case ROLE_SYSTEM_STATICTEXT: return UIA_TextControlTypeId;
435 case ROLE_SYSTEM_TEXT: return UIA_EditControlTypeId;
436 case ROLE_SYSTEM_CLOCK:
437 case ROLE_SYSTEM_BUTTONDROPDOWNGRID:
438 case ROLE_SYSTEM_PUSHBUTTON: return UIA_ButtonControlTypeId;
439 case ROLE_SYSTEM_CHECKBUTTON: return UIA_CheckBoxControlTypeId;
440 case ROLE_SYSTEM_RADIOBUTTON: return UIA_RadioButtonControlTypeId;
441 case ROLE_SYSTEM_COMBOBOX: return UIA_ComboBoxControlTypeId;
442 case ROLE_SYSTEM_PROGRESSBAR: return UIA_ProgressBarControlTypeId;
443 case ROLE_SYSTEM_SLIDER: return UIA_SliderControlTypeId;
444 case ROLE_SYSTEM_SPINBUTTON: return UIA_SpinnerControlTypeId;
445 case ROLE_SYSTEM_BUTTONMENU:
446 case ROLE_SYSTEM_MENUITEM: return UIA_MenuItemControlTypeId;
447 case ROLE_SYSTEM_PAGETABLIST: return UIA_TabControlTypeId;
448 case ROLE_SYSTEM_BUTTONDROPDOWN:
449 case ROLE_SYSTEM_SPLITBUTTON: return UIA_SplitButtonControlTypeId;
450 case ROLE_SYSTEM_SOUND:
451 case ROLE_SYSTEM_CURSOR:
452 case ROLE_SYSTEM_CARET:
453 case ROLE_SYSTEM_ALERT:
454 case ROLE_SYSTEM_CLIENT:
455 case ROLE_SYSTEM_CHART:
456 case ROLE_SYSTEM_DIALOG:
457 case ROLE_SYSTEM_BORDER:
458 case ROLE_SYSTEM_COLUMN:
459 case ROLE_SYSTEM_ROW:
460 case ROLE_SYSTEM_HELPBALLOON:
461 case ROLE_SYSTEM_CHARACTER:
462 case ROLE_SYSTEM_PROPERTYPAGE:
463 case ROLE_SYSTEM_DROPLIST:
464 case ROLE_SYSTEM_DIAL:
465 case ROLE_SYSTEM_HOTKEYFIELD:
466 case ROLE_SYSTEM_DIAGRAM:
467 case ROLE_SYSTEM_ANIMATION:
468 case ROLE_SYSTEM_EQUATION:
469 case ROLE_SYSTEM_WHITESPACE:
470 case ROLE_SYSTEM_IPADDRESS:
471 case ROLE_SYSTEM_OUTLINEBUTTON:
472 WARN("No UIA control type mapping for MSAA role %ld\n", role);
473 break;
475 default:
476 FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role);
477 break;
480 return 0;
484 * UiaProviderFromIAccessible IRawElementProviderSimple interface.
486 struct msaa_provider {
487 IRawElementProviderSimple IRawElementProviderSimple_iface;
488 IRawElementProviderFragment IRawElementProviderFragment_iface;
489 ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface;
490 LONG refcount;
492 IAccessible *acc;
493 IAccessible2 *ia2;
494 VARIANT cid;
495 HWND hwnd;
496 LONG control_type;
498 BOOL root_acc_check_ran;
499 BOOL is_root_acc;
501 IAccessible *parent;
502 INT child_pos;
505 static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
507 IAccessible *acc;
508 HRESULT hr;
510 if (msaa_prov->root_acc_check_ran)
511 return msaa_prov->is_root_acc;
513 msaa_prov->root_acc_check_ran = TRUE;
514 if (V_I4(&msaa_prov->cid) != CHILDID_SELF || msaa_prov->parent)
515 return FALSE;
517 hr = AccessibleObjectFromWindow(msaa_prov->hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
518 if (FAILED(hr))
519 return FALSE;
521 if (msaa_acc_compare(msaa_prov->acc, acc))
522 msaa_prov->is_root_acc = TRUE;
524 IAccessible_Release(acc);
525 return msaa_prov->is_root_acc;
528 static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface)
530 return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderSimple_iface);
533 HRESULT WINAPI msaa_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv)
535 struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
537 *ppv = NULL;
538 if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown))
539 *ppv = iface;
540 else if (IsEqualIID(riid, &IID_IRawElementProviderFragment))
541 *ppv = &msaa_prov->IRawElementProviderFragment_iface;
542 else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider))
543 *ppv = &msaa_prov->ILegacyIAccessibleProvider_iface;
544 else
545 return E_NOINTERFACE;
547 IRawElementProviderSimple_AddRef(iface);
548 return S_OK;
551 ULONG WINAPI msaa_provider_AddRef(IRawElementProviderSimple *iface)
553 struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
554 ULONG refcount = InterlockedIncrement(&msaa_prov->refcount);
556 TRACE("%p, refcount %ld\n", iface, refcount);
558 return refcount;
561 ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
563 struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
564 ULONG refcount = InterlockedDecrement(&msaa_prov->refcount);
566 TRACE("%p, refcount %ld\n", iface, refcount);
568 if (!refcount)
570 IAccessible_Release(msaa_prov->acc);
571 if (msaa_prov->parent)
572 IAccessible_Release(msaa_prov->parent);
573 if (msaa_prov->ia2)
574 IAccessible2_Release(msaa_prov->ia2);
575 free(msaa_prov);
578 return refcount;
581 HRESULT WINAPI msaa_provider_get_ProviderOptions(IRawElementProviderSimple *iface,
582 enum ProviderOptions *ret_val)
584 TRACE("%p, %p\n", iface, ret_val);
585 *ret_val = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
586 return S_OK;
589 HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface,
590 PATTERNID pattern_id, IUnknown **ret_val)
592 TRACE("%p, %d, %p\n", iface, pattern_id, ret_val);
594 *ret_val = NULL;
595 switch (pattern_id)
597 case UIA_LegacyIAccessiblePatternId:
598 return IRawElementProviderSimple_QueryInterface(iface, &IID_IUnknown, (void **)ret_val);
600 default:
601 FIXME("Unimplemented patternId %d\n", pattern_id);
602 break;
605 return S_OK;
608 HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
609 PROPERTYID prop_id, VARIANT *ret_val)
611 struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
612 HRESULT hr;
613 VARIANT v;
615 TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
617 VariantInit(ret_val);
618 VariantInit(&v);
619 switch (prop_id)
621 case UIA_ProviderDescriptionPropertyId:
622 V_VT(ret_val) = VT_BSTR;
623 V_BSTR(ret_val) = SysAllocString(L"Wine: MSAA Proxy");
624 break;
626 case UIA_ControlTypePropertyId:
627 if (!msaa_prov->control_type)
629 hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v);
630 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
631 msaa_prov->control_type = msaa_role_to_uia_control_type(V_I4(&v));
634 if (msaa_prov->control_type)
635 variant_init_i4(ret_val, msaa_prov->control_type);
637 break;
639 case UIA_HasKeyboardFocusPropertyId:
640 variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
641 STATE_SYSTEM_FOCUSED));
642 break;
644 case UIA_IsKeyboardFocusablePropertyId:
645 variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
646 STATE_SYSTEM_FOCUSABLE));
647 break;
649 case UIA_IsEnabledPropertyId:
650 variant_init_bool(ret_val, !msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
651 STATE_SYSTEM_UNAVAILABLE));
652 break;
654 case UIA_IsPasswordPropertyId:
655 variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
656 STATE_SYSTEM_PROTECTED));
657 break;
659 case UIA_NamePropertyId:
661 BSTR name;
663 hr = IAccessible_get_accName(msaa_prov->acc, msaa_prov->cid, &name);
664 if (SUCCEEDED(hr) && name)
666 V_VT(ret_val) = VT_BSTR;
667 V_BSTR(ret_val) = name;
669 break;
672 case UIA_IsOffscreenPropertyId:
674 RECT rect[2] = { 0 };
675 RECT intersect_rect;
676 LONG width, height;
678 variant_init_bool(ret_val, FALSE);
679 if (msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, STATE_SYSTEM_OFFSCREEN))
681 variant_init_bool(ret_val, TRUE);
682 break;
685 hr = IAccessible_accLocation(msaa_prov->acc, &rect[0].left, &rect[0].top, &width, &height, msaa_prov->cid);
686 if (FAILED(hr))
687 break;
689 rect[0].right = rect[0].left + width;
690 rect[0].bottom = rect[0].top + height;
691 SetLastError(NOERROR);
692 if (!GetClientRect(msaa_prov->hwnd, &rect[1]))
694 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
695 variant_init_bool(ret_val, TRUE);
696 break;
699 SetLastError(NOERROR);
700 if (!MapWindowPoints(msaa_prov->hwnd, NULL, (POINT *)&rect[1], 2) && GetLastError())
702 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
703 variant_init_bool(ret_val, TRUE);
704 break;
707 variant_init_bool(ret_val, !IntersectRect(&intersect_rect, &rect[0], &rect[1]));
708 break;
711 default:
712 FIXME("Unimplemented propertyId %d\n", prop_id);
713 break;
716 return S_OK;
719 HRESULT WINAPI msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface,
720 IRawElementProviderSimple **ret_val)
722 struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
724 TRACE("%p, %p\n", iface, ret_val);
726 *ret_val = NULL;
727 if (msaa_check_root_acc(msaa_prov))
728 return UiaHostProviderFromHwnd(msaa_prov->hwnd, ret_val);
730 return S_OK;
733 static const IRawElementProviderSimpleVtbl msaa_provider_vtbl = {
734 msaa_provider_QueryInterface,
735 msaa_provider_AddRef,
736 msaa_provider_Release,
737 msaa_provider_get_ProviderOptions,
738 msaa_provider_GetPatternProvider,
739 msaa_provider_GetPropertyValue,
740 msaa_provider_get_HostRawElementProvider,
744 * IRawElementProviderFragment interface for UiaProviderFromIAccessible
745 * providers.
747 static inline struct msaa_provider *impl_from_msaa_fragment(IRawElementProviderFragment *iface)
749 return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderFragment_iface);
752 static HRESULT WINAPI msaa_fragment_QueryInterface(IRawElementProviderFragment *iface, REFIID riid,
753 void **ppv)
755 struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
756 return IRawElementProviderSimple_QueryInterface(&msaa_prov->IRawElementProviderSimple_iface, riid, ppv);
759 static ULONG WINAPI msaa_fragment_AddRef(IRawElementProviderFragment *iface)
761 struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
762 return IRawElementProviderSimple_AddRef(&msaa_prov->IRawElementProviderSimple_iface);
765 static ULONG WINAPI msaa_fragment_Release(IRawElementProviderFragment *iface)
767 struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
768 return IRawElementProviderSimple_Release(&msaa_prov->IRawElementProviderSimple_iface);
771 static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
772 enum NavigateDirection direction, IRawElementProviderFragment **ret_val)
774 struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
775 LONG child_count, child_id, child_pos;
776 IRawElementProviderSimple *elprov;
777 IAccessible *acc;
778 HRESULT hr;
780 TRACE("%p, %d, %p\n", iface, direction, ret_val);
782 *ret_val = NULL;
783 switch (direction)
785 case NavigateDirection_Parent:
786 if (msaa_check_root_acc(msaa_prov))
787 break;
789 if (V_I4(&msaa_prov->cid) == CHILDID_SELF)
791 hr = msaa_acc_get_parent(msaa_prov->acc, &acc);
792 if (FAILED(hr) || !acc)
793 break;
795 else
796 acc = msaa_prov->acc;
798 hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, &elprov);
799 if (SUCCEEDED(hr))
801 struct msaa_provider *prov = impl_from_msaa_provider(elprov);
802 *ret_val = &prov->IRawElementProviderFragment_iface;
805 if (acc != msaa_prov->acc)
806 IAccessible_Release(acc);
808 break;
810 case NavigateDirection_FirstChild:
811 case NavigateDirection_LastChild:
812 if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
813 break;
815 hr = IAccessible_get_accChildCount(msaa_prov->acc, &child_count);
816 if (FAILED(hr) || !child_count)
817 break;
819 if (direction == NavigateDirection_FirstChild)
820 hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
821 &child_pos, TRUE);
822 else
823 hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
824 &child_pos, TRUE);
826 if (FAILED(hr) || !acc)
827 break;
829 hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov);
830 if (SUCCEEDED(hr))
832 struct msaa_provider *prov = impl_from_msaa_provider(elprov);
834 *ret_val = &prov->IRawElementProviderFragment_iface;
835 prov->parent = msaa_prov->acc;
836 IAccessible_AddRef(msaa_prov->acc);
837 if (acc != msaa_prov->acc)
838 prov->child_pos = child_pos;
839 else
840 prov->child_pos = child_id;
842 IAccessible_Release(acc);
844 break;
846 case NavigateDirection_NextSibling:
847 case NavigateDirection_PreviousSibling:
848 if (msaa_check_root_acc(msaa_prov))
849 break;
851 if (!msaa_prov->parent)
853 if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
855 msaa_prov->parent = msaa_prov->acc;
856 IAccessible_AddRef(msaa_prov->acc);
857 msaa_prov->child_pos = V_I4(&msaa_prov->cid);
859 else
861 hr = msaa_acc_get_child_pos(msaa_prov->acc, &acc, &child_pos);
862 if (FAILED(hr) || !acc)
863 break;
864 msaa_prov->parent = acc;
865 msaa_prov->child_pos = child_pos;
869 if (direction == NavigateDirection_NextSibling)
870 hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos + 1, DIR_FORWARD,
871 &acc, &child_id, &child_pos, TRUE);
872 else
873 hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos - 1, DIR_REVERSE,
874 &acc, &child_id, &child_pos, TRUE);
876 if (FAILED(hr) || !acc)
877 break;
879 hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov);
880 if (SUCCEEDED(hr))
882 struct msaa_provider *prov = impl_from_msaa_provider(elprov);
884 *ret_val = &prov->IRawElementProviderFragment_iface;
885 prov->parent = msaa_prov->parent;
886 IAccessible_AddRef(msaa_prov->parent);
887 if (acc != msaa_prov->acc)
888 prov->child_pos = child_pos;
889 else
890 prov->child_pos = child_id;
892 IAccessible_Release(acc);
894 break;
896 default:
897 FIXME("Invalid NavigateDirection %d\n", direction);
898 return E_INVALIDARG;
901 return S_OK;
904 static HRESULT WINAPI msaa_fragment_GetRuntimeId(IRawElementProviderFragment *iface,
905 SAFEARRAY **ret_val)
907 FIXME("%p, %p: stub!\n", iface, ret_val);
908 *ret_val = NULL;
909 return E_NOTIMPL;
912 static HRESULT WINAPI msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment *iface,
913 struct UiaRect *ret_val)
915 struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
916 LONG left, top, width, height;
917 HRESULT hr;
919 TRACE("%p, %p\n", iface, ret_val);
921 memset(ret_val, 0, sizeof(*ret_val));
924 * If this IAccessible is at the root of its HWND, the BaseHwnd provider
925 * will supply the bounding rectangle.
927 if (msaa_check_root_acc(msaa_prov))
928 return S_OK;
930 if (msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, STATE_SYSTEM_OFFSCREEN))
931 return S_OK;
933 hr = IAccessible_accLocation(msaa_prov->acc, &left, &top, &width, &height, msaa_prov->cid);
934 if (FAILED(hr))
935 return hr;
937 ret_val->left = left;
938 ret_val->top = top;
939 ret_val->width = width;
940 ret_val->height = height;
942 return S_OK;
945 static HRESULT WINAPI msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface,
946 SAFEARRAY **ret_val)
948 FIXME("%p, %p: stub!\n", iface, ret_val);
949 *ret_val = NULL;
950 return E_NOTIMPL;
953 static HRESULT WINAPI msaa_fragment_SetFocus(IRawElementProviderFragment *iface)
955 FIXME("%p: stub!\n", iface);
956 return E_NOTIMPL;
959 static HRESULT WINAPI msaa_fragment_get_FragmentRoot(IRawElementProviderFragment *iface,
960 IRawElementProviderFragmentRoot **ret_val)
962 FIXME("%p, %p: stub!\n", iface, ret_val);
963 *ret_val = NULL;
964 return E_NOTIMPL;
967 static const IRawElementProviderFragmentVtbl msaa_fragment_vtbl = {
968 msaa_fragment_QueryInterface,
969 msaa_fragment_AddRef,
970 msaa_fragment_Release,
971 msaa_fragment_Navigate,
972 msaa_fragment_GetRuntimeId,
973 msaa_fragment_get_BoundingRectangle,
974 msaa_fragment_GetEmbeddedFragmentRoots,
975 msaa_fragment_SetFocus,
976 msaa_fragment_get_FragmentRoot,
980 * ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible
981 * providers.
983 static inline struct msaa_provider *impl_from_msaa_acc_provider(ILegacyIAccessibleProvider *iface)
985 return CONTAINING_RECORD(iface, struct msaa_provider, ILegacyIAccessibleProvider_iface);
988 static HRESULT WINAPI msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider *iface, REFIID riid, void **ppv)
990 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
991 return IRawElementProviderSimple_QueryInterface(&msaa_prov->IRawElementProviderSimple_iface, riid, ppv);
994 static ULONG WINAPI msaa_acc_provider_AddRef(ILegacyIAccessibleProvider *iface)
996 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
997 return IRawElementProviderSimple_AddRef(&msaa_prov->IRawElementProviderSimple_iface);
1000 static ULONG WINAPI msaa_acc_provider_Release(ILegacyIAccessibleProvider *iface)
1002 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
1003 return IRawElementProviderSimple_Release(&msaa_prov->IRawElementProviderSimple_iface);
1006 static HRESULT WINAPI msaa_acc_provider_Select(ILegacyIAccessibleProvider *iface, LONG select_flags)
1008 FIXME("%p, %#lx: stub!\n", iface, select_flags);
1009 return E_NOTIMPL;
1012 static HRESULT WINAPI msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider *iface)
1014 FIXME("%p: stub!\n", iface);
1015 return E_NOTIMPL;
1018 static HRESULT WINAPI msaa_acc_provider_SetValue(ILegacyIAccessibleProvider *iface, LPCWSTR val)
1020 FIXME("%p, %p<%s>: stub!\n", iface, val, debugstr_w(val));
1021 return E_NOTIMPL;
1024 static HRESULT WINAPI msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider *iface,
1025 IAccessible **out_acc)
1027 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
1029 TRACE("%p, %p\n", iface, out_acc);
1031 IAccessible_AddRef(msaa_prov->acc);
1032 *out_acc = msaa_prov->acc;
1034 return S_OK;
1037 static HRESULT WINAPI msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider *iface, int *out_cid)
1039 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
1041 TRACE("%p, %p\n", iface, out_cid);
1042 *out_cid = V_I4(&msaa_prov->cid);
1044 return S_OK;
1047 static HRESULT WINAPI msaa_acc_provider_get_Name(ILegacyIAccessibleProvider *iface, BSTR *out_name)
1049 FIXME("%p, %p: stub!\n", iface, out_name);
1050 return E_NOTIMPL;
1053 static HRESULT WINAPI msaa_acc_provider_get_Value(ILegacyIAccessibleProvider *iface, BSTR *out_value)
1055 FIXME("%p, %p: stub!\n", iface, out_value);
1056 return E_NOTIMPL;
1059 static HRESULT WINAPI msaa_acc_provider_get_Description(ILegacyIAccessibleProvider *iface,
1060 BSTR *out_description)
1062 FIXME("%p, %p: stub!\n", iface, out_description);
1063 return E_NOTIMPL;
1066 static HRESULT WINAPI msaa_acc_provider_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role)
1068 struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
1069 HRESULT hr;
1070 VARIANT v;
1072 TRACE("%p, %p\n", iface, out_role);
1074 *out_role = 0;
1075 VariantInit(&v);
1076 hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v);
1077 if (SUCCEEDED(hr) && V_VT(&v) == VT_I4)
1078 *out_role = V_I4(&v);
1080 return S_OK;
1083 static HRESULT WINAPI msaa_acc_provider_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state)
1085 FIXME("%p, %p: stub!\n", iface, out_state);
1086 return E_NOTIMPL;
1089 static HRESULT WINAPI msaa_acc_provider_get_Help(ILegacyIAccessibleProvider *iface, BSTR *out_help)
1091 FIXME("%p, %p: stub!\n", iface, out_help);
1092 return E_NOTIMPL;
1095 static HRESULT WINAPI msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider *iface,
1096 BSTR *out_kbd_shortcut)
1098 FIXME("%p, %p: stub!\n", iface, out_kbd_shortcut);
1099 return E_NOTIMPL;
1102 static HRESULT WINAPI msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider *iface,
1103 SAFEARRAY **out_selected)
1105 FIXME("%p, %p: stub!\n", iface, out_selected);
1106 return E_NOTIMPL;
1109 static HRESULT WINAPI msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider *iface,
1110 BSTR *out_default_action)
1112 FIXME("%p, %p: stub!\n", iface, out_default_action);
1113 return E_NOTIMPL;
1116 static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl = {
1117 msaa_acc_provider_QueryInterface,
1118 msaa_acc_provider_AddRef,
1119 msaa_acc_provider_Release,
1120 msaa_acc_provider_Select,
1121 msaa_acc_provider_DoDefaultAction,
1122 msaa_acc_provider_SetValue,
1123 msaa_acc_provider_GetIAccessible,
1124 msaa_acc_provider_get_ChildId,
1125 msaa_acc_provider_get_Name,
1126 msaa_acc_provider_get_Value,
1127 msaa_acc_provider_get_Description,
1128 msaa_acc_provider_get_Role,
1129 msaa_acc_provider_get_State,
1130 msaa_acc_provider_get_Help,
1131 msaa_acc_provider_get_KeyboardShortcut,
1132 msaa_acc_provider_GetSelection,
1133 msaa_acc_provider_get_DefaultAction,
1136 HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL known_root_acc,
1137 IRawElementProviderSimple **elprov)
1139 struct msaa_provider *msaa_prov = calloc(1, sizeof(*msaa_prov));
1141 if (!msaa_prov)
1142 return E_OUTOFMEMORY;
1144 msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl;
1145 msaa_prov->IRawElementProviderFragment_iface.lpVtbl = &msaa_fragment_vtbl;
1146 msaa_prov->ILegacyIAccessibleProvider_iface.lpVtbl = &msaa_acc_provider_vtbl;
1147 msaa_prov->refcount = 1;
1148 variant_init_i4(&msaa_prov->cid, child_id);
1149 msaa_prov->acc = acc;
1150 IAccessible_AddRef(acc);
1151 msaa_prov->ia2 = msaa_acc_get_ia2(acc);
1153 if (!hwnd)
1155 HRESULT hr;
1157 hr = WindowFromAccessibleObject(acc, &msaa_prov->hwnd);
1158 if (FAILED(hr))
1159 WARN("WindowFromAccessibleObject failed with hr %#lx\n", hr);
1161 else
1162 msaa_prov->hwnd = hwnd;
1164 if (known_root_acc)
1165 msaa_prov->root_acc_check_ran = msaa_prov->is_root_acc = TRUE;
1167 *elprov = &msaa_prov->IRawElementProviderSimple_iface;
1169 return S_OK;
1172 /***********************************************************************
1173 * UiaProviderFromIAccessible (uiautomationcore.@)
1175 HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, LONG child_id, DWORD flags,
1176 IRawElementProviderSimple **elprov)
1178 IServiceProvider *serv_prov;
1179 HWND hwnd = NULL;
1180 HRESULT hr;
1182 TRACE("(%p, %ld, %#lx, %p)\n", acc, child_id, flags, elprov);
1184 if (elprov)
1185 *elprov = NULL;
1187 if (!elprov)
1188 return E_POINTER;
1189 if (!acc)
1190 return E_INVALIDARG;
1192 if (flags != UIA_PFIA_DEFAULT)
1194 FIXME("unsupported flags %#lx\n", flags);
1195 return E_NOTIMPL;
1198 hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
1199 if (SUCCEEDED(hr))
1201 IUnknown *unk;
1203 hr = IServiceProvider_QueryService(serv_prov, &IIS_IsOleaccProxy, &IID_IUnknown, (void **)&unk);
1204 if (SUCCEEDED(hr))
1206 WARN("Cannot wrap an oleacc proxy IAccessible!\n");
1207 IUnknown_Release(unk);
1208 IServiceProvider_Release(serv_prov);
1209 return E_INVALIDARG;
1212 IServiceProvider_Release(serv_prov);
1215 hr = WindowFromAccessibleObject(acc, &hwnd);
1216 if (FAILED(hr))
1217 return hr;
1218 if (!hwnd)
1219 return E_FAIL;
1221 return create_msaa_provider(acc, child_id, hwnd, FALSE, elprov);
1224 static HRESULT uia_get_hr_for_last_error(void)
1226 DWORD last_err = GetLastError();
1228 switch (last_err)
1230 case ERROR_INVALID_WINDOW_HANDLE:
1231 return UIA_E_ELEMENTNOTAVAILABLE;
1233 case ERROR_TIMEOUT:
1234 return UIA_E_TIMEOUT;
1236 default:
1237 return E_FAIL;
1241 #define UIA_DEFAULT_MSG_TIMEOUT 10000
1242 static HRESULT uia_send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT timeout, LRESULT *lres)
1244 *lres = 0;
1245 if (!SendMessageTimeoutW(hwnd, msg, wparam, lparam, SMTO_NORMAL, timeout, (PDWORD_PTR)lres))
1246 return uia_get_hr_for_last_error();
1248 return S_OK;
1251 static BOOL is_top_level_hwnd(HWND hwnd)
1253 return GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow();
1256 static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type)
1258 LONG_PTR style, ex_style;
1260 *control_type = 0;
1261 if ((ex_style = GetWindowLongPtrW(hwnd, GWL_EXSTYLE)) & WS_EX_APPWINDOW)
1263 *control_type = UIA_WindowControlTypeId;
1264 return S_OK;
1267 SetLastError(NO_ERROR);
1268 if (!(style = GetWindowLongPtrW(hwnd, GWL_STYLE)) && (GetLastError() != NO_ERROR))
1269 return uia_get_hr_for_last_error();
1272 * Non-caption HWNDs that are popups or tool windows aren't considered full
1273 * windows, only panes.
1275 if (((style & WS_CAPTION) != WS_CAPTION) && ((ex_style & WS_EX_TOOLWINDOW) || (style & WS_POPUP)))
1277 *control_type = UIA_PaneControlTypeId;
1278 return S_OK;
1281 /* Non top-level HWNDs are considered panes as well. */
1282 if (!is_top_level_hwnd(hwnd))
1283 *control_type = UIA_PaneControlTypeId;
1284 else
1285 *control_type = UIA_WindowControlTypeId;
1287 return S_OK;
1291 * Default ProviderType_BaseHwnd IRawElementProviderSimple interface.
1293 struct base_hwnd_provider {
1294 IRawElementProviderSimple IRawElementProviderSimple_iface;
1295 IRawElementProviderFragment IRawElementProviderFragment_iface;
1296 LONG refcount;
1298 HWND hwnd;
1301 static inline struct base_hwnd_provider *impl_from_base_hwnd_provider(IRawElementProviderSimple *iface)
1303 return CONTAINING_RECORD(iface, struct base_hwnd_provider, IRawElementProviderSimple_iface);
1306 static HRESULT WINAPI base_hwnd_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv)
1308 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface);
1310 *ppv = NULL;
1311 if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown))
1312 *ppv = iface;
1313 else if (IsEqualIID(riid, &IID_IRawElementProviderFragment))
1314 *ppv = &base_hwnd_prov->IRawElementProviderFragment_iface;
1315 else
1316 return E_NOINTERFACE;
1318 IRawElementProviderSimple_AddRef(iface);
1319 return S_OK;
1322 static ULONG WINAPI base_hwnd_provider_AddRef(IRawElementProviderSimple *iface)
1324 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface);
1325 ULONG refcount = InterlockedIncrement(&base_hwnd_prov->refcount);
1327 TRACE("%p, refcount %ld\n", iface, refcount);
1329 return refcount;
1332 static ULONG WINAPI base_hwnd_provider_Release(IRawElementProviderSimple *iface)
1334 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface);
1335 ULONG refcount = InterlockedDecrement(&base_hwnd_prov->refcount);
1337 TRACE("%p, refcount %ld\n", iface, refcount);
1339 if (!refcount)
1340 free(base_hwnd_prov);
1342 return refcount;
1345 static HRESULT WINAPI base_hwnd_provider_get_ProviderOptions(IRawElementProviderSimple *iface,
1346 enum ProviderOptions *ret_val)
1348 TRACE("%p, %p\n", iface, ret_val);
1349 *ret_val = ProviderOptions_ClientSideProvider;
1350 return S_OK;
1353 static HRESULT WINAPI base_hwnd_provider_GetPatternProvider(IRawElementProviderSimple *iface,
1354 PATTERNID pattern_id, IUnknown **ret_val)
1356 FIXME("%p, %d, %p: stub\n", iface, pattern_id, ret_val);
1357 *ret_val = NULL;
1358 return E_NOTIMPL;
1361 static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSimple *iface,
1362 PROPERTYID prop_id, VARIANT *ret_val)
1364 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface);
1365 HRESULT hr = S_OK;
1367 TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
1369 VariantInit(ret_val);
1370 if (!IsWindow(base_hwnd_prov->hwnd))
1371 return UIA_E_ELEMENTNOTAVAILABLE;
1373 switch (prop_id)
1375 case UIA_ProviderDescriptionPropertyId:
1376 V_VT(ret_val) = VT_BSTR;
1377 V_BSTR(ret_val) = SysAllocString(L"Wine: HWND Proxy");
1378 break;
1380 case UIA_NativeWindowHandlePropertyId:
1381 V_VT(ret_val) = VT_I4;
1382 V_I4(ret_val) = HandleToUlong(base_hwnd_prov->hwnd);
1383 break;
1385 case UIA_ProcessIdPropertyId:
1387 DWORD pid;
1389 if (!GetWindowThreadProcessId(base_hwnd_prov->hwnd, &pid))
1390 return UIA_E_ELEMENTNOTAVAILABLE;
1392 V_VT(ret_val) = VT_I4;
1393 V_I4(ret_val) = pid;
1394 break;
1397 case UIA_ClassNamePropertyId:
1399 WCHAR buf[256] = { 0 };
1401 if (!GetClassNameW(base_hwnd_prov->hwnd, buf, ARRAY_SIZE(buf)))
1402 hr = uia_get_hr_for_last_error();
1403 else
1405 V_VT(ret_val) = VT_BSTR;
1406 V_BSTR(ret_val) = SysAllocString(buf);
1408 break;
1411 case UIA_NamePropertyId:
1413 LRESULT lres;
1415 V_VT(ret_val) = VT_BSTR;
1416 V_BSTR(ret_val) = SysAllocString(L"");
1417 hr = uia_send_message_timeout(base_hwnd_prov->hwnd, WM_GETTEXTLENGTH, 0, 0, UIA_DEFAULT_MSG_TIMEOUT, &lres);
1418 if (FAILED(hr) || !lres)
1419 break;
1421 if (!SysReAllocStringLen(&V_BSTR(ret_val), NULL, lres))
1423 hr = E_OUTOFMEMORY;
1424 break;
1427 hr = uia_send_message_timeout(base_hwnd_prov->hwnd, WM_GETTEXT, SysStringLen(V_BSTR(ret_val)) + 1,
1428 (LPARAM)V_BSTR(ret_val), UIA_DEFAULT_MSG_TIMEOUT, &lres);
1429 break;
1432 case UIA_ControlTypePropertyId:
1434 int control_type;
1436 hr = get_uia_control_type_for_hwnd(base_hwnd_prov->hwnd, &control_type);
1437 if (SUCCEEDED(hr))
1439 V_VT(ret_val) = VT_I4;
1440 V_I4(ret_val) = control_type;
1442 break;
1445 default:
1446 break;
1449 if (FAILED(hr))
1450 VariantClear(ret_val);
1452 return hr;
1455 static HRESULT WINAPI base_hwnd_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface,
1456 IRawElementProviderSimple **ret_val)
1458 TRACE("%p, %p\n", iface, ret_val);
1459 *ret_val = NULL;
1460 return S_OK;
1463 static const IRawElementProviderSimpleVtbl base_hwnd_provider_vtbl = {
1464 base_hwnd_provider_QueryInterface,
1465 base_hwnd_provider_AddRef,
1466 base_hwnd_provider_Release,
1467 base_hwnd_provider_get_ProviderOptions,
1468 base_hwnd_provider_GetPatternProvider,
1469 base_hwnd_provider_GetPropertyValue,
1470 base_hwnd_provider_get_HostRawElementProvider,
1474 * IRawElementProviderFragment interface for default ProviderType_BaseHwnd
1475 * providers.
1477 static inline struct base_hwnd_provider *impl_from_base_hwnd_fragment(IRawElementProviderFragment *iface)
1479 return CONTAINING_RECORD(iface, struct base_hwnd_provider, IRawElementProviderFragment_iface);
1482 static HRESULT WINAPI base_hwnd_fragment_QueryInterface(IRawElementProviderFragment *iface, REFIID riid,
1483 void **ppv)
1485 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface);
1486 return IRawElementProviderSimple_QueryInterface(&base_hwnd_prov->IRawElementProviderSimple_iface, riid, ppv);
1489 static ULONG WINAPI base_hwnd_fragment_AddRef(IRawElementProviderFragment *iface)
1491 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface);
1492 return IRawElementProviderSimple_AddRef(&base_hwnd_prov->IRawElementProviderSimple_iface);
1495 static ULONG WINAPI base_hwnd_fragment_Release(IRawElementProviderFragment *iface)
1497 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface);
1498 return IRawElementProviderSimple_Release(&base_hwnd_prov->IRawElementProviderSimple_iface);
1501 static HRESULT WINAPI base_hwnd_fragment_Navigate(IRawElementProviderFragment *iface,
1502 enum NavigateDirection direction, IRawElementProviderFragment **ret_val)
1504 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface);
1505 IRawElementProviderSimple *elprov = NULL;
1506 HRESULT hr = S_OK;
1508 TRACE("%p, %d, %p\n", iface, direction, ret_val);
1510 *ret_val = NULL;
1512 switch (direction)
1514 case NavigateDirection_Parent:
1516 HWND parent, owner;
1519 * Top level owned windows have their owner window as a parent instead
1520 * of the desktop window.
1522 if (is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER)))
1523 parent = owner;
1524 else
1525 parent = GetAncestor(base_hwnd_prov->hwnd, GA_PARENT);
1527 if (parent)
1528 hr = create_base_hwnd_provider(parent, &elprov);
1529 break;
1532 case NavigateDirection_FirstChild:
1533 case NavigateDirection_LastChild:
1534 case NavigateDirection_PreviousSibling:
1535 case NavigateDirection_NextSibling:
1536 FIXME("Unimplemented NavigateDirection %d\n", direction);
1537 return E_NOTIMPL;
1539 default:
1540 FIXME("Invalid NavigateDirection %d\n", direction);
1541 return E_INVALIDARG;
1544 if (elprov)
1546 hr = IRawElementProviderSimple_QueryInterface(elprov, &IID_IRawElementProviderFragment, (void **)ret_val);
1547 IRawElementProviderSimple_Release(elprov);
1550 return hr;
1553 static HRESULT WINAPI base_hwnd_fragment_GetRuntimeId(IRawElementProviderFragment *iface,
1554 SAFEARRAY **ret_val)
1556 FIXME("%p, %p: stub!\n", iface, ret_val);
1557 *ret_val = NULL;
1558 return E_NOTIMPL;
1561 static HRESULT WINAPI base_hwnd_fragment_get_BoundingRectangle(IRawElementProviderFragment *iface,
1562 struct UiaRect *ret_val)
1564 struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_fragment(iface);
1565 RECT rect = { 0 };
1567 TRACE("%p, %p\n", iface, ret_val);
1569 memset(ret_val, 0, sizeof(*ret_val));
1571 /* Top level minimized window - Return empty rect. */
1572 if (is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd))
1573 return S_OK;
1575 if (!GetWindowRect(base_hwnd_prov->hwnd, &rect))
1576 return uia_get_hr_for_last_error();
1578 ret_val->left = rect.left;
1579 ret_val->top = rect.top;
1580 ret_val->width = (rect.right - rect.left);
1581 ret_val->height = (rect.bottom - rect.top);
1583 return S_OK;
1586 static HRESULT WINAPI base_hwnd_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface,
1587 SAFEARRAY **ret_val)
1589 FIXME("%p, %p: stub!\n", iface, ret_val);
1590 *ret_val = NULL;
1591 return E_NOTIMPL;
1594 static HRESULT WINAPI base_hwnd_fragment_SetFocus(IRawElementProviderFragment *iface)
1596 FIXME("%p: stub!\n", iface);
1597 return E_NOTIMPL;
1600 static HRESULT WINAPI base_hwnd_fragment_get_FragmentRoot(IRawElementProviderFragment *iface,
1601 IRawElementProviderFragmentRoot **ret_val)
1603 FIXME("%p, %p: stub!\n", iface, ret_val);
1604 *ret_val = NULL;
1605 return E_NOTIMPL;
1608 static const IRawElementProviderFragmentVtbl base_hwnd_fragment_vtbl = {
1609 base_hwnd_fragment_QueryInterface,
1610 base_hwnd_fragment_AddRef,
1611 base_hwnd_fragment_Release,
1612 base_hwnd_fragment_Navigate,
1613 base_hwnd_fragment_GetRuntimeId,
1614 base_hwnd_fragment_get_BoundingRectangle,
1615 base_hwnd_fragment_GetEmbeddedFragmentRoots,
1616 base_hwnd_fragment_SetFocus,
1617 base_hwnd_fragment_get_FragmentRoot,
1620 HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov)
1622 struct base_hwnd_provider *base_hwnd_prov;
1624 *elprov = NULL;
1626 if (!hwnd)
1627 return E_INVALIDARG;
1629 if (!IsWindow(hwnd))
1630 return UIA_E_ELEMENTNOTAVAILABLE;
1632 if (!(base_hwnd_prov = calloc(1, sizeof(*base_hwnd_prov))))
1633 return E_OUTOFMEMORY;
1635 base_hwnd_prov->IRawElementProviderSimple_iface.lpVtbl = &base_hwnd_provider_vtbl;
1636 base_hwnd_prov->IRawElementProviderFragment_iface.lpVtbl = &base_hwnd_fragment_vtbl;
1637 base_hwnd_prov->refcount = 1;
1638 base_hwnd_prov->hwnd = hwnd;
1639 *elprov = &base_hwnd_prov->IRawElementProviderSimple_iface;
1641 return S_OK;
1645 * UI Automation provider thread functions.
1647 struct uia_provider_thread
1649 struct rb_tree node_map;
1650 struct list nodes_list;
1651 HANDLE hthread;
1652 HWND hwnd;
1653 LONG ref;
1656 static struct uia_provider_thread provider_thread;
1657 static CRITICAL_SECTION provider_thread_cs;
1658 static CRITICAL_SECTION_DEBUG provider_thread_cs_debug =
1660 0, 0, &provider_thread_cs,
1661 { &provider_thread_cs_debug.ProcessLocksList, &provider_thread_cs_debug.ProcessLocksList },
1662 0, 0, { (DWORD_PTR)(__FILE__ ": provider_thread_cs") }
1664 static CRITICAL_SECTION provider_thread_cs = { &provider_thread_cs_debug, -1, 0, 0, 0, 0 };
1666 struct uia_provider_thread_map_entry
1668 struct rb_entry entry;
1670 SAFEARRAY *runtime_id;
1671 struct list nodes_list;
1674 static int uia_runtime_id_compare(const void *key, const struct rb_entry *entry)
1676 struct uia_provider_thread_map_entry *prov_entry = RB_ENTRY_VALUE(entry, struct uia_provider_thread_map_entry, entry);
1677 return uia_compare_safearrays(prov_entry->runtime_id, (SAFEARRAY *)key, UIAutomationType_IntArray);
1680 void uia_provider_thread_remove_node(HUIANODE node)
1682 struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node);
1684 TRACE("Removing node %p\n", node);
1686 EnterCriticalSection(&provider_thread_cs);
1688 list_remove(&node_data->prov_thread_list_entry);
1689 list_init(&node_data->prov_thread_list_entry);
1690 if (!list_empty(&node_data->node_map_list_entry))
1692 list_remove(&node_data->node_map_list_entry);
1693 list_init(&node_data->node_map_list_entry);
1694 if (list_empty(&node_data->map->nodes_list))
1696 rb_remove(&provider_thread.node_map, &node_data->map->entry);
1697 SafeArrayDestroy(node_data->map->runtime_id);
1698 free(node_data->map);
1700 node_data->map = NULL;
1703 LeaveCriticalSection(&provider_thread_cs);
1706 static void uia_provider_thread_disconnect_node(SAFEARRAY *sa)
1708 struct rb_entry *rb_entry;
1710 EnterCriticalSection(&provider_thread_cs);
1712 /* Provider thread hasn't been started, no nodes to disconnect. */
1713 if (!provider_thread.ref)
1714 goto exit;
1716 rb_entry = rb_get(&provider_thread.node_map, sa);
1717 if (rb_entry)
1719 struct uia_provider_thread_map_entry *prov_map;
1720 struct list *cursor, *cursor2;
1721 struct uia_node *node_data;
1723 prov_map = RB_ENTRY_VALUE(rb_entry, struct uia_provider_thread_map_entry, entry);
1724 LIST_FOR_EACH_SAFE(cursor, cursor2, &prov_map->nodes_list)
1726 node_data = LIST_ENTRY(cursor, struct uia_node, node_map_list_entry);
1728 list_remove(cursor);
1729 list_remove(&node_data->prov_thread_list_entry);
1730 list_init(&node_data->prov_thread_list_entry);
1731 list_init(&node_data->node_map_list_entry);
1732 node_data->map = NULL;
1734 IWineUiaNode_disconnect(&node_data->IWineUiaNode_iface);
1737 rb_remove(&provider_thread.node_map, &prov_map->entry);
1738 SafeArrayDestroy(prov_map->runtime_id);
1739 free(prov_map);
1742 exit:
1743 LeaveCriticalSection(&provider_thread_cs);
1746 static HRESULT uia_provider_thread_add_node(HUIANODE node, SAFEARRAY *rt_id)
1748 struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node);
1749 int prov_type = get_node_provider_type_at_idx(node_data, 0);
1750 struct uia_provider *prov_data;
1751 HRESULT hr = S_OK;
1753 prov_data = impl_from_IWineUiaProvider(node_data->prov[prov_type]);
1754 node_data->nested_node = prov_data->return_nested_node = prov_data->refuse_hwnd_node_providers = TRUE;
1756 TRACE("Adding node %p\n", node);
1758 EnterCriticalSection(&provider_thread_cs);
1759 list_add_tail(&provider_thread.nodes_list, &node_data->prov_thread_list_entry);
1761 /* If we have a runtime ID, create an entry in the rb tree. */
1762 if (rt_id)
1764 struct uia_provider_thread_map_entry *prov_map;
1765 struct rb_entry *rb_entry;
1767 if ((rb_entry = rb_get(&provider_thread.node_map, rt_id)))
1768 prov_map = RB_ENTRY_VALUE(rb_entry, struct uia_provider_thread_map_entry, entry);
1769 else
1771 prov_map = calloc(1, sizeof(*prov_map));
1772 if (!prov_map)
1774 hr = E_OUTOFMEMORY;
1775 goto exit;
1778 hr = SafeArrayCopy(rt_id, &prov_map->runtime_id);
1779 if (FAILED(hr))
1781 free(prov_map);
1782 goto exit;
1784 list_init(&prov_map->nodes_list);
1785 rb_put(&provider_thread.node_map, prov_map->runtime_id, &prov_map->entry);
1788 list_add_tail(&prov_map->nodes_list, &node_data->node_map_list_entry);
1789 node_data->map = prov_map;
1792 exit:
1793 LeaveCriticalSection(&provider_thread_cs);
1795 return hr;
1798 #define WM_GET_OBJECT_UIA_NODE (WM_USER + 1)
1799 #define WM_UIA_PROVIDER_THREAD_STOP (WM_USER + 2)
1800 static LRESULT CALLBACK uia_provider_thread_msg_proc(HWND hwnd, UINT msg, WPARAM wparam,
1801 LPARAM lparam)
1803 switch (msg)
1805 case WM_GET_OBJECT_UIA_NODE:
1807 SAFEARRAY *rt_id = (SAFEARRAY *)wparam;
1808 HUIANODE node = (HUIANODE)lparam;
1809 LRESULT lr;
1811 if (FAILED(uia_provider_thread_add_node(node, rt_id)))
1813 WARN("Failed to add node %p to provider thread list.\n", node);
1814 return 0;
1818 * LresultFromObject returns an index into the global atom string table,
1819 * which has a valid range of 0xc000-0xffff.
1821 lr = LresultFromObject(&IID_IWineUiaNode, 0, (IUnknown *)node);
1822 if ((lr > 0xffff) || (lr < 0xc000))
1824 WARN("Got invalid lresult %Ix\n", lr);
1825 lr = 0;
1828 return lr;
1831 default:
1832 break;
1835 return DefWindowProcW(hwnd, msg, wparam, lparam);
1838 static DWORD WINAPI uia_provider_thread_proc(void *arg)
1840 HANDLE initialized_event = arg;
1841 HWND hwnd;
1842 MSG msg;
1844 CoInitializeEx(NULL, COINIT_MULTITHREADED);
1845 hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
1846 if (!hwnd)
1848 WARN("CreateWindow failed: %ld\n", GetLastError());
1849 CoUninitialize();
1850 FreeLibraryAndExitThread(huia_module, 1);
1853 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)uia_provider_thread_msg_proc);
1854 provider_thread.hwnd = hwnd;
1856 /* Initialization complete, thread can now process window messages. */
1857 SetEvent(initialized_event);
1858 TRACE("Provider thread started.\n");
1859 while (GetMessageW(&msg, NULL, 0, 0))
1861 if (msg.message == WM_UIA_PROVIDER_THREAD_STOP)
1862 break;
1863 TranslateMessage(&msg);
1864 DispatchMessageW(&msg);
1867 TRACE("Shutting down UI Automation provider thread.\n");
1869 DestroyWindow(hwnd);
1870 CoUninitialize();
1871 FreeLibraryAndExitThread(huia_module, 0);
1874 static BOOL uia_start_provider_thread(void)
1876 BOOL started = TRUE;
1878 EnterCriticalSection(&provider_thread_cs);
1879 if (++provider_thread.ref == 1)
1881 HANDLE ready_event;
1882 HANDLE events[2];
1883 HMODULE hmodule;
1884 DWORD wait_obj;
1886 /* Increment DLL reference count. */
1887 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
1888 (const WCHAR *)uia_start_provider_thread, &hmodule);
1890 list_init(&provider_thread.nodes_list);
1891 rb_init(&provider_thread.node_map, uia_runtime_id_compare);
1892 events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1893 if (!(provider_thread.hthread = CreateThread(NULL, 0, uia_provider_thread_proc,
1894 ready_event, 0, NULL)))
1896 FreeLibrary(hmodule);
1897 started = FALSE;
1898 goto exit;
1901 events[1] = provider_thread.hthread;
1902 wait_obj = WaitForMultipleObjects(2, events, FALSE, INFINITE);
1903 if (wait_obj != WAIT_OBJECT_0)
1905 CloseHandle(provider_thread.hthread);
1906 started = FALSE;
1909 exit:
1910 CloseHandle(ready_event);
1911 if (!started)
1913 WARN("Failed to start provider thread\n");
1914 memset(&provider_thread, 0, sizeof(provider_thread));
1918 LeaveCriticalSection(&provider_thread_cs);
1919 return started;
1922 void uia_stop_provider_thread(void)
1924 EnterCriticalSection(&provider_thread_cs);
1925 if (!--provider_thread.ref)
1927 PostMessageW(provider_thread.hwnd, WM_UIA_PROVIDER_THREAD_STOP, 0, 0);
1928 CloseHandle(provider_thread.hthread);
1929 if (!list_empty(&provider_thread.nodes_list))
1930 ERR("Provider thread shutdown with nodes still in the list\n");
1931 memset(&provider_thread, 0, sizeof(provider_thread));
1933 LeaveCriticalSection(&provider_thread_cs);
1937 * Pass our IWineUiaNode interface to the provider thread for marshaling. UI
1938 * Automation has to work regardless of whether or not COM is initialized on
1939 * the thread calling UiaReturnRawElementProvider.
1941 LRESULT uia_lresult_from_node(HUIANODE huianode)
1943 SAFEARRAY *rt_id;
1944 LRESULT lr = 0;
1945 HRESULT hr;
1947 hr = UiaGetRuntimeId(huianode, &rt_id);
1948 if (SUCCEEDED(hr) && uia_start_provider_thread())
1949 lr = SendMessageW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, (WPARAM)rt_id, (LPARAM)huianode);
1951 if (FAILED(hr))
1952 WARN("UiaGetRuntimeId failed with hr %#lx\n", hr);
1955 * LresultFromObject increases refcnt by 1. If LresultFromObject
1956 * failed or wasn't called, this is expected to release the node.
1958 UiaNodeRelease(huianode);
1959 SafeArrayDestroy(rt_id);
1960 return lr;
1963 /***********************************************************************
1964 * UiaReturnRawElementProvider (uiautomationcore.@)
1966 LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wparam,
1967 LPARAM lparam, IRawElementProviderSimple *elprov)
1969 HUIANODE node;
1970 HRESULT hr;
1972 TRACE("(%p, %Ix, %#Ix, %p)\n", hwnd, wparam, lparam, elprov);
1974 if (!wparam && !lparam && !elprov)
1976 FIXME("UIA-to-MSAA bridge not implemented, no provider map to free.\n");
1977 return 0;
1980 if (lparam != UiaRootObjectId)
1982 FIXME("Unsupported object id %Id, ignoring.\n", lparam);
1983 return 0;
1986 hr = create_uia_node_from_elprov(elprov, &node, FALSE);
1987 if (FAILED(hr))
1989 WARN("Failed to create HUIANODE with hr %#lx\n", hr);
1990 return 0;
1993 return uia_lresult_from_node(node);
1996 /***********************************************************************
1997 * UiaDisconnectProvider (uiautomationcore.@)
1999 HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov)
2001 SAFEARRAY *sa;
2002 HUIANODE node;
2003 HRESULT hr;
2005 TRACE("(%p)\n", elprov);
2007 hr = create_uia_node_from_elprov(elprov, &node, FALSE);
2008 if (FAILED(hr))
2009 return hr;
2011 hr = UiaGetRuntimeId(node, &sa);
2012 UiaNodeRelease(node);
2013 if (FAILED(hr))
2014 return hr;
2016 if (!sa)
2017 return E_INVALIDARG;
2019 uia_provider_thread_disconnect_node(sa);
2021 SafeArrayDestroy(sa);
2023 return S_OK;