mirror of
git://source.winehq.org/git/wine.git
synced 2024-07-24 00:36:22 +00:00
1129 lines
33 KiB
C
1129 lines
33 KiB
C
/*
|
|
* Copyright 2022 Connor McAdams for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "uiautomation.h"
|
|
#include "ocidl.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/heap.h"
|
|
#include "initguid.h"
|
|
#include "wine/iaccessible2.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
|
|
|
|
DEFINE_GUID(SID_AccFromDAWrapper, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
|
|
|
|
static void variant_init_i4(VARIANT *v, int val)
|
|
{
|
|
V_VT(v) = VT_I4;
|
|
V_I4(v) = val;
|
|
}
|
|
|
|
static void variant_init_bool(VARIANT *v, BOOL val)
|
|
{
|
|
V_VT(v) = VT_BOOL;
|
|
V_BOOL(v) = val ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
|
|
static BOOL msaa_check_acc_state(IAccessible *acc, VARIANT cid, ULONG flag)
|
|
{
|
|
HRESULT hr;
|
|
VARIANT v;
|
|
|
|
VariantInit(&v);
|
|
hr = IAccessible_get_accState(acc, cid, &v);
|
|
if (SUCCEEDED(hr) && V_VT(&v) == VT_I4 && (V_I4(&v) & flag))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static IAccessible2 *msaa_acc_get_ia2(IAccessible *acc)
|
|
{
|
|
IServiceProvider *serv_prov;
|
|
IAccessible2 *ia2 = NULL;
|
|
HRESULT hr;
|
|
|
|
hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IServiceProvider_QueryService(serv_prov, &IID_IAccessible2, &IID_IAccessible2, (void **)&ia2);
|
|
IServiceProvider_Release(serv_prov);
|
|
if (SUCCEEDED(hr) && ia2)
|
|
return ia2;
|
|
}
|
|
|
|
hr = IAccessible_QueryInterface(acc, &IID_IAccessible2, (void **)&ia2);
|
|
if (SUCCEEDED(hr) && ia2)
|
|
return ia2;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static IAccessible *msaa_acc_da_unwrap(IAccessible *acc)
|
|
{
|
|
IServiceProvider *sp;
|
|
IAccessible *acc2;
|
|
HRESULT hr;
|
|
|
|
hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void**)&sp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IServiceProvider_QueryService(sp, &SID_AccFromDAWrapper, &IID_IAccessible, (void**)&acc2);
|
|
IServiceProvider_Release(sp);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && acc2)
|
|
return acc2;
|
|
|
|
IAccessible_AddRef(acc);
|
|
return acc;
|
|
}
|
|
|
|
/*
|
|
* Compare role, state, child count, and location properties of the two
|
|
* IAccessibles. If all four are successfully retrieved and are equal, this is
|
|
* considered a match.
|
|
*/
|
|
static HRESULT msaa_acc_prop_match(IAccessible *acc, IAccessible *acc2)
|
|
{
|
|
BOOL role_match, state_match, child_count_match, location_match;
|
|
LONG child_count[2], left[2], top[2], width[2], height[2];
|
|
VARIANT cid, v, v2;
|
|
HRESULT hr, hr2;
|
|
|
|
role_match = state_match = child_count_match = location_match = FALSE;
|
|
variant_init_i4(&cid, CHILDID_SELF);
|
|
hr = IAccessible_get_accRole(acc, cid, &v);
|
|
if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
|
|
{
|
|
VariantInit(&v2);
|
|
hr = IAccessible_get_accRole(acc2, cid, &v2);
|
|
if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
|
|
{
|
|
if (V_I4(&v) != V_I4(&v2))
|
|
return E_FAIL;
|
|
|
|
role_match = TRUE;
|
|
}
|
|
}
|
|
|
|
VariantInit(&v);
|
|
hr = IAccessible_get_accState(acc, cid, &v);
|
|
if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
|
|
{
|
|
VariantInit(&v2);
|
|
hr = IAccessible_get_accState(acc2, cid, &v2);
|
|
if (SUCCEEDED(hr) && (V_VT(&v2) == VT_I4))
|
|
{
|
|
if (V_I4(&v) != V_I4(&v2))
|
|
return E_FAIL;
|
|
|
|
state_match = TRUE;
|
|
}
|
|
}
|
|
|
|
hr = IAccessible_get_accChildCount(acc, &child_count[0]);
|
|
hr2 = IAccessible_get_accChildCount(acc2, &child_count[1]);
|
|
if (SUCCEEDED(hr) && SUCCEEDED(hr2))
|
|
{
|
|
if (child_count[0] != child_count[1])
|
|
return E_FAIL;
|
|
|
|
child_count_match = TRUE;
|
|
}
|
|
|
|
hr = IAccessible_accLocation(acc, &left[0], &top[0], &width[0], &height[0], cid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IAccessible_accLocation(acc2, &left[1], &top[1], &width[1], &height[1], cid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ((left[0] != left[1]) || (top[0] != top[1]) || (width[0] != width[1]) ||
|
|
(height[0] != height[1]))
|
|
return E_FAIL;
|
|
|
|
location_match = TRUE;
|
|
}
|
|
}
|
|
|
|
if (role_match && state_match && child_count_match && location_match)
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
static BOOL msaa_acc_compare(IAccessible *acc, IAccessible *acc2)
|
|
{
|
|
IAccessible2 *ia2[2] = { NULL, NULL };
|
|
IUnknown *unk, *unk2;
|
|
BOOL matched = FALSE;
|
|
LONG unique_id[2];
|
|
BSTR name[2];
|
|
VARIANT cid;
|
|
HRESULT hr;
|
|
|
|
acc = msaa_acc_da_unwrap(acc);
|
|
acc2 = msaa_acc_da_unwrap(acc2);
|
|
IAccessible_QueryInterface(acc, &IID_IUnknown, (void**)&unk);
|
|
IAccessible_QueryInterface(acc2, &IID_IUnknown, (void**)&unk2);
|
|
if (unk == unk2)
|
|
{
|
|
matched = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
ia2[0] = msaa_acc_get_ia2(acc);
|
|
ia2[1] = msaa_acc_get_ia2(acc2);
|
|
if (!ia2[0] != !ia2[1])
|
|
goto exit;
|
|
if (ia2[0])
|
|
{
|
|
hr = IAccessible2_get_uniqueID(ia2[0], &unique_id[0]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IAccessible2_get_uniqueID(ia2[1], &unique_id[1]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (unique_id[0] == unique_id[1])
|
|
matched = TRUE;
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = msaa_acc_prop_match(acc, acc2);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
if (hr == S_OK)
|
|
matched = TRUE;
|
|
|
|
variant_init_i4(&cid, CHILDID_SELF);
|
|
hr = IAccessible_get_accName(acc, cid, &name[0]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IAccessible_get_accName(acc2, cid, &name[1]);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!name[0] && !name[1])
|
|
matched = TRUE;
|
|
else if (!name[0] || !name[1])
|
|
matched = FALSE;
|
|
else
|
|
{
|
|
if (!wcscmp(name[0], name[1]))
|
|
matched = TRUE;
|
|
else
|
|
matched = FALSE;
|
|
}
|
|
|
|
SysFreeString(name[1]);
|
|
}
|
|
|
|
SysFreeString(name[0]);
|
|
}
|
|
|
|
exit:
|
|
IUnknown_Release(unk);
|
|
IUnknown_Release(unk2);
|
|
IAccessible_Release(acc);
|
|
IAccessible_Release(acc2);
|
|
if (ia2[0])
|
|
IAccessible2_Release(ia2[0]);
|
|
if (ia2[1])
|
|
IAccessible2_Release(ia2[1]);
|
|
|
|
return matched;
|
|
}
|
|
|
|
static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
|
|
{
|
|
IDispatch *disp = NULL;
|
|
HRESULT hr;
|
|
|
|
*parent = NULL;
|
|
hr = IAccessible_get_accParent(acc, &disp);
|
|
if (FAILED(hr) || !disp)
|
|
return hr;
|
|
|
|
hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void**)parent);
|
|
IDispatch_Release(disp);
|
|
return hr;
|
|
}
|
|
|
|
#define DIR_FORWARD 0
|
|
#define DIR_REVERSE 1
|
|
static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG direction,
|
|
IAccessible **child, LONG *child_id, LONG *child_pos, BOOL check_visible)
|
|
{
|
|
LONG child_count, cur_pos;
|
|
IDispatch *disp;
|
|
VARIANT cid;
|
|
HRESULT hr;
|
|
|
|
*child = NULL;
|
|
*child_id = 0;
|
|
cur_pos = start_pos;
|
|
while (1)
|
|
{
|
|
hr = IAccessible_get_accChildCount(acc, &child_count);
|
|
if (FAILED(hr) || (cur_pos > child_count))
|
|
break;
|
|
|
|
variant_init_i4(&cid, cur_pos);
|
|
hr = IAccessible_get_accChild(acc, cid, &disp);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
if (!check_visible || !msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
|
|
{
|
|
*child = acc;
|
|
*child_id = *child_pos = cur_pos;
|
|
IAccessible_AddRef(acc);
|
|
return S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IAccessible *acc_child = NULL;
|
|
|
|
hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc_child);
|
|
IDispatch_Release(disp);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
variant_init_i4(&cid, CHILDID_SELF);
|
|
if (!check_visible || !msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
|
|
{
|
|
*child = acc_child;
|
|
*child_id = CHILDID_SELF;
|
|
*child_pos = cur_pos;
|
|
return S_OK;
|
|
}
|
|
|
|
IAccessible_Release(acc_child);
|
|
}
|
|
|
|
if (direction == DIR_FORWARD)
|
|
cur_pos++;
|
|
else
|
|
cur_pos--;
|
|
|
|
if ((cur_pos > child_count) || (cur_pos <= 0))
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT msaa_acc_get_child_pos(IAccessible *acc, IAccessible **out_parent,
|
|
LONG *out_child_pos)
|
|
{
|
|
LONG child_count, child_id, child_pos, match_pos;
|
|
IAccessible *child, *parent, *match, **children;
|
|
HRESULT hr;
|
|
int i;
|
|
|
|
*out_parent = NULL;
|
|
*out_child_pos = 0;
|
|
hr = msaa_acc_get_parent(acc, &parent);
|
|
if (FAILED(hr) || !parent)
|
|
return hr;
|
|
|
|
hr = IAccessible_get_accChildCount(parent, &child_count);
|
|
if (FAILED(hr) || !child_count)
|
|
{
|
|
IAccessible_Release(parent);
|
|
return hr;
|
|
}
|
|
|
|
children = heap_alloc_zero(sizeof(*children) * child_count);
|
|
if (!children)
|
|
return E_OUTOFMEMORY;
|
|
|
|
match = NULL;
|
|
for (i = 0; i < child_count; i++)
|
|
{
|
|
hr = msaa_acc_get_next_child(parent, i + 1, DIR_FORWARD, &child, &child_id, &child_pos, FALSE);
|
|
if (FAILED(hr) || !child)
|
|
goto exit;
|
|
|
|
if (child != parent)
|
|
children[i] = child;
|
|
else
|
|
IAccessible_Release(child);
|
|
}
|
|
|
|
for (i = 0; i < child_count; i++)
|
|
{
|
|
if (!children[i])
|
|
continue;
|
|
|
|
if (msaa_acc_compare(acc, children[i]))
|
|
{
|
|
if (!match)
|
|
{
|
|
match = children[i];
|
|
match_pos = i + 1;
|
|
}
|
|
/* Can't have more than one IAccessible match. */
|
|
else
|
|
{
|
|
match = NULL;
|
|
match_pos = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (match)
|
|
{
|
|
*out_parent = parent;
|
|
*out_child_pos = match_pos;
|
|
}
|
|
else
|
|
IAccessible_Release(parent);
|
|
|
|
for (i = 0; i < child_count; i++)
|
|
{
|
|
if (children[i])
|
|
IAccessible_Release(children[i]);
|
|
}
|
|
|
|
heap_free(children);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static LONG msaa_role_to_uia_control_type(LONG role)
|
|
{
|
|
switch (role)
|
|
{
|
|
case ROLE_SYSTEM_TITLEBAR: return UIA_TitleBarControlTypeId;
|
|
case ROLE_SYSTEM_MENUBAR: return UIA_MenuBarControlTypeId;
|
|
case ROLE_SYSTEM_SCROLLBAR: return UIA_ScrollBarControlTypeId;
|
|
case ROLE_SYSTEM_INDICATOR:
|
|
case ROLE_SYSTEM_GRIP: return UIA_ThumbControlTypeId;
|
|
case ROLE_SYSTEM_APPLICATION:
|
|
case ROLE_SYSTEM_WINDOW: return UIA_WindowControlTypeId;
|
|
case ROLE_SYSTEM_MENUPOPUP: return UIA_MenuControlTypeId;
|
|
case ROLE_SYSTEM_TOOLTIP: return UIA_ToolTipControlTypeId;
|
|
case ROLE_SYSTEM_DOCUMENT: return UIA_DocumentControlTypeId;
|
|
case ROLE_SYSTEM_PANE: return UIA_PaneControlTypeId;
|
|
case ROLE_SYSTEM_GROUPING: return UIA_GroupControlTypeId;
|
|
case ROLE_SYSTEM_SEPARATOR: return UIA_SeparatorControlTypeId;
|
|
case ROLE_SYSTEM_TOOLBAR: return UIA_ToolBarControlTypeId;
|
|
case ROLE_SYSTEM_STATUSBAR: return UIA_StatusBarControlTypeId;
|
|
case ROLE_SYSTEM_TABLE: return UIA_TableControlTypeId;
|
|
case ROLE_SYSTEM_COLUMNHEADER:
|
|
case ROLE_SYSTEM_ROWHEADER: return UIA_HeaderControlTypeId;
|
|
case ROLE_SYSTEM_CELL: return UIA_DataItemControlTypeId;
|
|
case ROLE_SYSTEM_LINK: return UIA_HyperlinkControlTypeId;
|
|
case ROLE_SYSTEM_LIST: return UIA_ListControlTypeId;
|
|
case ROLE_SYSTEM_LISTITEM: return UIA_ListItemControlTypeId;
|
|
case ROLE_SYSTEM_OUTLINE: return UIA_TreeControlTypeId;
|
|
case ROLE_SYSTEM_OUTLINEITEM: return UIA_TreeItemControlTypeId;
|
|
case ROLE_SYSTEM_PAGETAB: return UIA_TabItemControlTypeId;
|
|
case ROLE_SYSTEM_GRAPHIC: return UIA_ImageControlTypeId;
|
|
case ROLE_SYSTEM_STATICTEXT: return UIA_TextControlTypeId;
|
|
case ROLE_SYSTEM_TEXT: return UIA_EditControlTypeId;
|
|
case ROLE_SYSTEM_CLOCK:
|
|
case ROLE_SYSTEM_BUTTONDROPDOWNGRID:
|
|
case ROLE_SYSTEM_PUSHBUTTON: return UIA_ButtonControlTypeId;
|
|
case ROLE_SYSTEM_CHECKBUTTON: return UIA_CheckBoxControlTypeId;
|
|
case ROLE_SYSTEM_RADIOBUTTON: return UIA_RadioButtonControlTypeId;
|
|
case ROLE_SYSTEM_COMBOBOX: return UIA_ComboBoxControlTypeId;
|
|
case ROLE_SYSTEM_PROGRESSBAR: return UIA_ProgressBarControlTypeId;
|
|
case ROLE_SYSTEM_SLIDER: return UIA_SliderControlTypeId;
|
|
case ROLE_SYSTEM_SPINBUTTON: return UIA_SpinnerControlTypeId;
|
|
case ROLE_SYSTEM_BUTTONMENU:
|
|
case ROLE_SYSTEM_MENUITEM: return UIA_MenuItemControlTypeId;
|
|
case ROLE_SYSTEM_PAGETABLIST: return UIA_TabControlTypeId;
|
|
case ROLE_SYSTEM_BUTTONDROPDOWN:
|
|
case ROLE_SYSTEM_SPLITBUTTON: return UIA_SplitButtonControlTypeId;
|
|
case ROLE_SYSTEM_SOUND:
|
|
case ROLE_SYSTEM_CURSOR:
|
|
case ROLE_SYSTEM_CARET:
|
|
case ROLE_SYSTEM_ALERT:
|
|
case ROLE_SYSTEM_CLIENT:
|
|
case ROLE_SYSTEM_CHART:
|
|
case ROLE_SYSTEM_DIALOG:
|
|
case ROLE_SYSTEM_BORDER:
|
|
case ROLE_SYSTEM_COLUMN:
|
|
case ROLE_SYSTEM_ROW:
|
|
case ROLE_SYSTEM_HELPBALLOON:
|
|
case ROLE_SYSTEM_CHARACTER:
|
|
case ROLE_SYSTEM_PROPERTYPAGE:
|
|
case ROLE_SYSTEM_DROPLIST:
|
|
case ROLE_SYSTEM_DIAL:
|
|
case ROLE_SYSTEM_HOTKEYFIELD:
|
|
case ROLE_SYSTEM_DIAGRAM:
|
|
case ROLE_SYSTEM_ANIMATION:
|
|
case ROLE_SYSTEM_EQUATION:
|
|
case ROLE_SYSTEM_WHITESPACE:
|
|
case ROLE_SYSTEM_IPADDRESS:
|
|
case ROLE_SYSTEM_OUTLINEBUTTON:
|
|
WARN("No UIA control type mapping for MSAA role %ld\n", role);
|
|
break;
|
|
|
|
default:
|
|
FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* UiaProviderFromIAccessible IRawElementProviderSimple interface.
|
|
*/
|
|
struct msaa_provider {
|
|
IRawElementProviderSimple IRawElementProviderSimple_iface;
|
|
IRawElementProviderFragment IRawElementProviderFragment_iface;
|
|
ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface;
|
|
LONG refcount;
|
|
|
|
IAccessible *acc;
|
|
IAccessible2 *ia2;
|
|
VARIANT cid;
|
|
HWND hwnd;
|
|
LONG control_type;
|
|
|
|
BOOL root_acc_check_ran;
|
|
BOOL is_root_acc;
|
|
|
|
IAccessible *parent;
|
|
INT child_pos;
|
|
};
|
|
|
|
static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
|
|
{
|
|
IAccessible *acc;
|
|
HRESULT hr;
|
|
|
|
if (msaa_prov->root_acc_check_ran)
|
|
return msaa_prov->is_root_acc;
|
|
|
|
msaa_prov->root_acc_check_ran = TRUE;
|
|
if (V_I4(&msaa_prov->cid) != CHILDID_SELF || msaa_prov->parent)
|
|
return FALSE;
|
|
|
|
hr = AccessibleObjectFromWindow(msaa_prov->hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
if (msaa_acc_compare(msaa_prov->acc, acc))
|
|
msaa_prov->is_root_acc = TRUE;
|
|
|
|
IAccessible_Release(acc);
|
|
return msaa_prov->is_root_acc;
|
|
}
|
|
|
|
static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderSimple_iface);
|
|
}
|
|
|
|
HRESULT WINAPI msaa_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
|
|
|
|
*ppv = NULL;
|
|
if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown))
|
|
*ppv = iface;
|
|
else if (IsEqualIID(riid, &IID_IRawElementProviderFragment))
|
|
*ppv = &msaa_prov->IRawElementProviderFragment_iface;
|
|
else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider))
|
|
*ppv = &msaa_prov->ILegacyIAccessibleProvider_iface;
|
|
else
|
|
return E_NOINTERFACE;
|
|
|
|
IRawElementProviderSimple_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG WINAPI msaa_provider_AddRef(IRawElementProviderSimple *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
|
|
ULONG refcount = InterlockedIncrement(&msaa_prov->refcount);
|
|
|
|
TRACE("%p, refcount %ld\n", iface, refcount);
|
|
|
|
return refcount;
|
|
}
|
|
|
|
ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
|
|
ULONG refcount = InterlockedDecrement(&msaa_prov->refcount);
|
|
|
|
TRACE("%p, refcount %ld\n", iface, refcount);
|
|
|
|
if (!refcount)
|
|
{
|
|
IAccessible_Release(msaa_prov->acc);
|
|
if (msaa_prov->parent)
|
|
IAccessible_Release(msaa_prov->parent);
|
|
if (msaa_prov->ia2)
|
|
IAccessible2_Release(msaa_prov->ia2);
|
|
heap_free(msaa_prov);
|
|
}
|
|
|
|
return refcount;
|
|
}
|
|
|
|
HRESULT WINAPI msaa_provider_get_ProviderOptions(IRawElementProviderSimple *iface,
|
|
enum ProviderOptions *ret_val)
|
|
{
|
|
TRACE("%p, %p\n", iface, ret_val);
|
|
*ret_val = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface,
|
|
PATTERNID pattern_id, IUnknown **ret_val)
|
|
{
|
|
TRACE("%p, %d, %p\n", iface, pattern_id, ret_val);
|
|
|
|
*ret_val = NULL;
|
|
switch (pattern_id)
|
|
{
|
|
case UIA_LegacyIAccessiblePatternId:
|
|
return IRawElementProviderSimple_QueryInterface(iface, &IID_IUnknown, (void **)ret_val);
|
|
|
|
default:
|
|
FIXME("Unimplemented patternId %d\n", pattern_id);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
|
|
PROPERTYID prop_id, VARIANT *ret_val)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
|
|
HRESULT hr;
|
|
VARIANT v;
|
|
|
|
TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
|
|
|
|
VariantInit(ret_val);
|
|
VariantInit(&v);
|
|
switch (prop_id)
|
|
{
|
|
case UIA_ProviderDescriptionPropertyId:
|
|
V_VT(ret_val) = VT_BSTR;
|
|
V_BSTR(ret_val) = SysAllocString(L"Wine: MSAA Proxy");
|
|
break;
|
|
|
|
case UIA_ControlTypePropertyId:
|
|
if (!msaa_prov->control_type)
|
|
{
|
|
hr = IAccessible_get_accRole(msaa_prov->acc, msaa_prov->cid, &v);
|
|
if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
|
|
msaa_prov->control_type = msaa_role_to_uia_control_type(V_I4(&v));
|
|
}
|
|
|
|
if (msaa_prov->control_type)
|
|
variant_init_i4(ret_val, msaa_prov->control_type);
|
|
|
|
break;
|
|
|
|
case UIA_HasKeyboardFocusPropertyId:
|
|
variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
|
|
STATE_SYSTEM_FOCUSED));
|
|
break;
|
|
|
|
case UIA_IsKeyboardFocusablePropertyId:
|
|
variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
|
|
STATE_SYSTEM_FOCUSABLE));
|
|
break;
|
|
|
|
case UIA_IsEnabledPropertyId:
|
|
variant_init_bool(ret_val, !msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
|
|
STATE_SYSTEM_UNAVAILABLE));
|
|
break;
|
|
|
|
case UIA_IsPasswordPropertyId:
|
|
variant_init_bool(ret_val, msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid,
|
|
STATE_SYSTEM_PROTECTED));
|
|
break;
|
|
|
|
default:
|
|
FIXME("Unimplemented propertyId %d\n", prop_id);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface,
|
|
IRawElementProviderSimple **ret_val)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
|
|
|
|
TRACE("%p, %p\n", iface, ret_val);
|
|
|
|
*ret_val = NULL;
|
|
if (msaa_check_root_acc(msaa_prov))
|
|
return UiaHostProviderFromHwnd(msaa_prov->hwnd, ret_val);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static const IRawElementProviderSimpleVtbl msaa_provider_vtbl = {
|
|
msaa_provider_QueryInterface,
|
|
msaa_provider_AddRef,
|
|
msaa_provider_Release,
|
|
msaa_provider_get_ProviderOptions,
|
|
msaa_provider_GetPatternProvider,
|
|
msaa_provider_GetPropertyValue,
|
|
msaa_provider_get_HostRawElementProvider,
|
|
};
|
|
|
|
/*
|
|
* IRawElementProviderFragment interface for UiaProviderFromIAccessible
|
|
* providers.
|
|
*/
|
|
static inline struct msaa_provider *impl_from_msaa_fragment(IRawElementProviderFragment *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderFragment_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_QueryInterface(IRawElementProviderFragment *iface, REFIID riid,
|
|
void **ppv)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
|
|
return IRawElementProviderSimple_QueryInterface(&msaa_prov->IRawElementProviderSimple_iface, riid, ppv);
|
|
}
|
|
|
|
static ULONG WINAPI msaa_fragment_AddRef(IRawElementProviderFragment *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
|
|
return IRawElementProviderSimple_AddRef(&msaa_prov->IRawElementProviderSimple_iface);
|
|
}
|
|
|
|
static ULONG WINAPI msaa_fragment_Release(IRawElementProviderFragment *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
|
|
return IRawElementProviderSimple_Release(&msaa_prov->IRawElementProviderSimple_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
|
|
enum NavigateDirection direction, IRawElementProviderFragment **ret_val)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
|
|
LONG child_count, child_id, child_pos;
|
|
IRawElementProviderSimple *elprov;
|
|
IAccessible *acc;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %d, %p\n", iface, direction, ret_val);
|
|
|
|
*ret_val = NULL;
|
|
switch (direction)
|
|
{
|
|
case NavigateDirection_Parent:
|
|
if (msaa_check_root_acc(msaa_prov))
|
|
break;
|
|
|
|
if (V_I4(&msaa_prov->cid) == CHILDID_SELF)
|
|
{
|
|
hr = msaa_acc_get_parent(msaa_prov->acc, &acc);
|
|
if (FAILED(hr) || !acc)
|
|
break;
|
|
}
|
|
else
|
|
acc = msaa_prov->acc;
|
|
|
|
hr = UiaProviderFromIAccessible(acc, CHILDID_SELF, 0, &elprov);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
struct msaa_provider *prov = impl_from_msaa_provider(elprov);
|
|
*ret_val = &prov->IRawElementProviderFragment_iface;
|
|
}
|
|
|
|
if (acc != msaa_prov->acc)
|
|
IAccessible_Release(acc);
|
|
|
|
break;
|
|
|
|
case NavigateDirection_FirstChild:
|
|
case NavigateDirection_LastChild:
|
|
if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
|
|
break;
|
|
|
|
hr = IAccessible_get_accChildCount(msaa_prov->acc, &child_count);
|
|
if (FAILED(hr) || !child_count)
|
|
break;
|
|
|
|
if (direction == NavigateDirection_FirstChild)
|
|
hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
|
|
&child_pos, TRUE);
|
|
else
|
|
hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
|
|
&child_pos, TRUE);
|
|
|
|
if (FAILED(hr) || !acc)
|
|
break;
|
|
|
|
hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
struct msaa_provider *prov = impl_from_msaa_provider(elprov);
|
|
|
|
*ret_val = &prov->IRawElementProviderFragment_iface;
|
|
prov->parent = msaa_prov->acc;
|
|
IAccessible_AddRef(msaa_prov->acc);
|
|
if (acc != msaa_prov->acc)
|
|
prov->child_pos = child_pos;
|
|
else
|
|
prov->child_pos = child_id;
|
|
}
|
|
IAccessible_Release(acc);
|
|
|
|
break;
|
|
|
|
case NavigateDirection_NextSibling:
|
|
case NavigateDirection_PreviousSibling:
|
|
if (msaa_check_root_acc(msaa_prov))
|
|
break;
|
|
|
|
if (!msaa_prov->parent)
|
|
{
|
|
if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
|
|
{
|
|
msaa_prov->parent = msaa_prov->acc;
|
|
IAccessible_AddRef(msaa_prov->acc);
|
|
msaa_prov->child_pos = V_I4(&msaa_prov->cid);
|
|
}
|
|
else
|
|
{
|
|
hr = msaa_acc_get_child_pos(msaa_prov->acc, &acc, &child_pos);
|
|
if (FAILED(hr) || !acc)
|
|
break;
|
|
msaa_prov->parent = acc;
|
|
msaa_prov->child_pos = child_pos;
|
|
}
|
|
}
|
|
|
|
if (direction == NavigateDirection_NextSibling)
|
|
hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos + 1, DIR_FORWARD,
|
|
&acc, &child_id, &child_pos, TRUE);
|
|
else
|
|
hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos - 1, DIR_REVERSE,
|
|
&acc, &child_id, &child_pos, TRUE);
|
|
|
|
if (FAILED(hr) || !acc)
|
|
break;
|
|
|
|
hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
struct msaa_provider *prov = impl_from_msaa_provider(elprov);
|
|
|
|
*ret_val = &prov->IRawElementProviderFragment_iface;
|
|
prov->parent = msaa_prov->parent;
|
|
IAccessible_AddRef(msaa_prov->parent);
|
|
if (acc != msaa_prov->acc)
|
|
prov->child_pos = child_pos;
|
|
else
|
|
prov->child_pos = child_id;
|
|
}
|
|
IAccessible_Release(acc);
|
|
|
|
break;
|
|
|
|
default:
|
|
FIXME("Invalid NavigateDirection %d\n", direction);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_GetRuntimeId(IRawElementProviderFragment *iface,
|
|
SAFEARRAY **ret_val)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, ret_val);
|
|
*ret_val = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment *iface,
|
|
struct UiaRect *ret_val)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, ret_val);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment *iface,
|
|
SAFEARRAY **ret_val)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, ret_val);
|
|
*ret_val = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_SetFocus(IRawElementProviderFragment *iface)
|
|
{
|
|
FIXME("%p: stub!\n", iface);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_fragment_get_FragmentRoot(IRawElementProviderFragment *iface,
|
|
IRawElementProviderFragmentRoot **ret_val)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, ret_val);
|
|
*ret_val = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IRawElementProviderFragmentVtbl msaa_fragment_vtbl = {
|
|
msaa_fragment_QueryInterface,
|
|
msaa_fragment_AddRef,
|
|
msaa_fragment_Release,
|
|
msaa_fragment_Navigate,
|
|
msaa_fragment_GetRuntimeId,
|
|
msaa_fragment_get_BoundingRectangle,
|
|
msaa_fragment_GetEmbeddedFragmentRoots,
|
|
msaa_fragment_SetFocus,
|
|
msaa_fragment_get_FragmentRoot,
|
|
};
|
|
|
|
/*
|
|
* ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible
|
|
* providers.
|
|
*/
|
|
static inline struct msaa_provider *impl_from_msaa_acc_provider(ILegacyIAccessibleProvider *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct msaa_provider, ILegacyIAccessibleProvider_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider *iface, REFIID riid, void **ppv)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
|
|
return IRawElementProviderSimple_QueryInterface(&msaa_prov->IRawElementProviderSimple_iface, riid, ppv);
|
|
}
|
|
|
|
static ULONG WINAPI msaa_acc_provider_AddRef(ILegacyIAccessibleProvider *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
|
|
return IRawElementProviderSimple_AddRef(&msaa_prov->IRawElementProviderSimple_iface);
|
|
}
|
|
|
|
static ULONG WINAPI msaa_acc_provider_Release(ILegacyIAccessibleProvider *iface)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
|
|
return IRawElementProviderSimple_Release(&msaa_prov->IRawElementProviderSimple_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_Select(ILegacyIAccessibleProvider *iface, LONG select_flags)
|
|
{
|
|
FIXME("%p, %#lx: stub!\n", iface, select_flags);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider *iface)
|
|
{
|
|
FIXME("%p: stub!\n", iface);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_SetValue(ILegacyIAccessibleProvider *iface, LPCWSTR val)
|
|
{
|
|
FIXME("%p, %p<%s>: stub!\n", iface, val, debugstr_w(val));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider *iface,
|
|
IAccessible **out_acc)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
|
|
|
|
TRACE("%p, %p\n", iface, out_acc);
|
|
|
|
IAccessible_AddRef(msaa_prov->acc);
|
|
*out_acc = msaa_prov->acc;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider *iface, int *out_cid)
|
|
{
|
|
struct msaa_provider *msaa_prov = impl_from_msaa_acc_provider(iface);
|
|
|
|
TRACE("%p, %p\n", iface, out_cid);
|
|
*out_cid = V_I4(&msaa_prov->cid);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_Name(ILegacyIAccessibleProvider *iface, BSTR *out_name)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_name);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_Value(ILegacyIAccessibleProvider *iface, BSTR *out_value)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_value);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_Description(ILegacyIAccessibleProvider *iface,
|
|
BSTR *out_description)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_description);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_Role(ILegacyIAccessibleProvider *iface, DWORD *out_role)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_role);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_State(ILegacyIAccessibleProvider *iface, DWORD *out_state)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_state);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_Help(ILegacyIAccessibleProvider *iface, BSTR *out_help)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_help);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider *iface,
|
|
BSTR *out_kbd_shortcut)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_kbd_shortcut);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider *iface,
|
|
SAFEARRAY **out_selected)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_selected);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider *iface,
|
|
BSTR *out_default_action)
|
|
{
|
|
FIXME("%p, %p: stub!\n", iface, out_default_action);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl = {
|
|
msaa_acc_provider_QueryInterface,
|
|
msaa_acc_provider_AddRef,
|
|
msaa_acc_provider_Release,
|
|
msaa_acc_provider_Select,
|
|
msaa_acc_provider_DoDefaultAction,
|
|
msaa_acc_provider_SetValue,
|
|
msaa_acc_provider_GetIAccessible,
|
|
msaa_acc_provider_get_ChildId,
|
|
msaa_acc_provider_get_Name,
|
|
msaa_acc_provider_get_Value,
|
|
msaa_acc_provider_get_Description,
|
|
msaa_acc_provider_get_Role,
|
|
msaa_acc_provider_get_State,
|
|
msaa_acc_provider_get_Help,
|
|
msaa_acc_provider_get_KeyboardShortcut,
|
|
msaa_acc_provider_GetSelection,
|
|
msaa_acc_provider_get_DefaultAction,
|
|
};
|
|
|
|
/***********************************************************************
|
|
* UiaProviderFromIAccessible (uiautomationcore.@)
|
|
*/
|
|
HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD flags,
|
|
IRawElementProviderSimple **elprov)
|
|
{
|
|
struct msaa_provider *msaa_prov;
|
|
IServiceProvider *serv_prov;
|
|
HWND hwnd = NULL;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p, %ld, %#lx, %p)\n", acc, child_id, flags, elprov);
|
|
|
|
if (elprov)
|
|
*elprov = NULL;
|
|
|
|
if (!elprov)
|
|
return E_POINTER;
|
|
if (!acc)
|
|
return E_INVALIDARG;
|
|
|
|
if (flags != UIA_PFIA_DEFAULT)
|
|
{
|
|
FIXME("unsupported flags %#lx\n", flags);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IUnknown *unk;
|
|
|
|
hr = IServiceProvider_QueryService(serv_prov, &IIS_IsOleaccProxy, &IID_IUnknown, (void **)&unk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WARN("Cannot wrap an oleacc proxy IAccessible!\n");
|
|
IUnknown_Release(unk);
|
|
IServiceProvider_Release(serv_prov);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
IServiceProvider_Release(serv_prov);
|
|
}
|
|
|
|
hr = WindowFromAccessibleObject(acc, &hwnd);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
if (!hwnd)
|
|
return E_FAIL;
|
|
|
|
msaa_prov = heap_alloc_zero(sizeof(*msaa_prov));
|
|
if (!msaa_prov)
|
|
return E_OUTOFMEMORY;
|
|
|
|
msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl;
|
|
msaa_prov->IRawElementProviderFragment_iface.lpVtbl = &msaa_fragment_vtbl;
|
|
msaa_prov->ILegacyIAccessibleProvider_iface.lpVtbl = &msaa_acc_provider_vtbl;
|
|
msaa_prov->refcount = 1;
|
|
msaa_prov->hwnd = hwnd;
|
|
variant_init_i4(&msaa_prov->cid, child_id);
|
|
msaa_prov->acc = acc;
|
|
IAccessible_AddRef(acc);
|
|
msaa_prov->ia2 = msaa_acc_get_ia2(acc);
|
|
*elprov = &msaa_prov->IRawElementProviderSimple_iface;
|
|
|
|
return S_OK;
|
|
}
|