Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
-This might technically be a v5, but since the AccessibleObjectFromEvent
patch was accepted and not the tests, I've not marked this v5.
dlls/oleacc/tests/main.c | 994 +++++++++++++++++++++++++++++++++++++++
1 file changed, 994 insertions(+)
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c
index ce44e06abe4..34498d2b3fe 100644
--- a/dlls/oleacc/tests/main.c
+++ b/dlls/oleacc/tests/main.c
@@ -304,6 +304,772 @@ static IAccessibleVtbl AccessibleVtbl = {
static IAccessible Accessible = {&AccessibleVtbl};
+/*
+ * Create an accessibility tree that looks like this:
+ * --ACC_TREE_OBJ_ID_ROOT (Full object)
+ * ----ACC_TREE_OBJ_ID_CHILD_0 (Full object)
+ * ----ACC_TREE_OBJ_ID_CHILD_1 (Full object)
+ * ------ACC_TREE_OBJ_ID_CHILD_1_0 (Simple element)
+ * ------ACC_TREE_OBJ_ID_CHILD_1_1 (Full object)
+ * ----ACC_TREE_OBJ_ID_CHILD_2 (Simple element)
+ */
+enum {
+ ACC_TREE_OBJ_ID_NONE = -1,
+ ACC_TREE_OBJ_ID_ROOT = 1,
+ ACC_TREE_OBJ_ID_CHILD_0,
+ ACC_TREE_OBJ_ID_CHILD_1,
+ ACC_TREE_OBJ_ID_CHILD_1_0,
+ ACC_TREE_OBJ_ID_CHILD_1_1,
+ ACC_TREE_OBJ_ID_CHILD_2,
+};
+
+typedef struct {
+ const WCHAR *name;
+
+ INT parent_id, child_id, child_pos;
+ BOOL is_simple_elem;
+ UINT child_count;
+} acc_tree_object_info;
+
+typedef struct {
+ IAccessible IAccessible_iface;
+ IEnumVARIANT IEnumVARIANT_iface;
+
+ LONG ref;
+
+ const acc_tree_object_info *info;
+
+ HWND hwnd;
+ UINT enum_pos;
+
+ IAccessible *parent;
+ IAccessible *children[3];
+} AccTreeObject;
+
+static AccTreeObject *object_tree = NULL;
+static BOOL acc_query_interface_test_fail = FALSE;
+
+const acc_tree_object_info acc_from_event_obj_tree[] = {
+ { .name = L"acc_tree_root",
+ .parent_id = ACC_TREE_OBJ_ID_NONE,
+ .child_id = ACC_TREE_OBJ_ID_ROOT,
+ .child_pos = 0,
+ .child_count = 3,
+ .is_simple_elem = FALSE,
+ },
+ { .name = L"acc_tree_child_0",
+ .parent_id = ACC_TREE_OBJ_ID_ROOT,
+ .child_id = ACC_TREE_OBJ_ID_CHILD_0,
+ .child_pos = 0,
+ .child_count = 0,
+ .is_simple_elem = FALSE,
+ },
+ { .name = L"acc_tree_child_1",
+ .parent_id = ACC_TREE_OBJ_ID_ROOT,
+ .child_id = ACC_TREE_OBJ_ID_CHILD_1,
+ .child_pos = 1,
+ .child_count = 2,
+ .is_simple_elem = FALSE,
+ },
+ { .name = L"acc_tree_child_1_0",
+ .parent_id = ACC_TREE_OBJ_ID_CHILD_1,
+ .child_id = ACC_TREE_OBJ_ID_CHILD_1_0,
+ .child_pos = 0,
+ .child_count = 0,
+ .is_simple_elem = TRUE,
+ },
+ { .name = L"acc_tree_child_1_1",
+ .parent_id = ACC_TREE_OBJ_ID_CHILD_1,
+ .child_id = ACC_TREE_OBJ_ID_CHILD_1_1,
+ .child_pos = 1,
+ .child_count = 0,
+ .is_simple_elem = FALSE,
+ },
+ { .name = L"acc_tree_child_2",
+ .parent_id = ACC_TREE_OBJ_ID_ROOT,
+ .child_id = ACC_TREE_OBJ_ID_CHILD_2,
+ .child_pos = 2,
+ .child_count = 0,
+ .is_simple_elem = TRUE,
+ },
+};
+
+/*
+ * AccTreeObj IAccessible Vtbl.
+ */
+static inline AccTreeObject* impl_from_AccTreeObject(IAccessible *iface)
+{
+ return CONTAINING_RECORD(iface, AccTreeObject, IAccessible_iface);
+}
+
+static IAccessible *get_acc_tree_obj_child(IAccessible *iface, VARIANT child_id)
+{
+ AccTreeObject *obj = impl_from_AccTreeObject(iface);
+ IAccessible *acc = NULL;
+ LONG i;
+
+ if (V_VT(&child_id) != VT_I4)
+ return NULL;
+
+ if (V_I4(&child_id) == CHILDID_SELF)
+ {
+ IAccessible_AddRef(iface);
+ return iface;
+ }
+
+ for (i = 0; i < obj->info->child_count; i++)
+ {
+ AccTreeObject *child = impl_from_AccTreeObject(obj->children[i]);
+
+ if (child->info->child_id == V_I4(&child_id))
+ {
+ acc = &child->IAccessible_iface;
+ break;
+ }
+ }
+
+ if (acc)
+ IAccessible_AddRef(acc);
+
+ return acc;
+}
+
+static void check_acc_tree_obj_for_child(IAccessible *acc,
+ INT child_id, IAccessible **found_acc, VARIANT *found_vid)
+{
+ IDispatch *disp_child;
+ VARIANT vid;
+ HRESULT hr;
+
+ V_VT(&vid) = VT_I4;
+ V_I4(&vid) = child_id;
+ hr = IAccessible_get_accChild(acc, vid, &disp_child);
+ if (SUCCEEDED(hr))
+ {
+ /*
+ * If S_FALSE is returned, the childID was found, but it's a simple
+ * element.
+ */
+ if (hr == S_FALSE)
+ {
+ *found_acc = acc;
+ V_VT(found_vid) = VT_I4;
+ V_I4(found_vid) = child_id;
+ }
+ else
+ {
+ IDispatch_QueryInterface(disp_child, &IID_IAccessible,
+ (void **)found_acc);
+ V_VT(found_vid) = VT_I4;
+ V_I4(found_vid) = CHILDID_SELF;
+ IDispatch_Release(disp_child);
+ }
+ }
+}
+
+static void search_acc_tree_for_child_enumVARIANT(IAccessible *acc,
+ INT child_id, IAccessible **found_acc, VARIANT *found_vid)
+{
+ LONG child_cnt, i, rcv;
+ VARIANT *children;
+ HRESULT hr;
+
+ IAccessible_get_accChildCount(acc, &child_cnt);
+ if (!child_cnt)
+ return;
+
+ check_acc_tree_obj_for_child(acc, child_id, found_acc, found_vid);
+ if (*found_acc)
+ return;
+
+ children = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof(*children) * child_cnt);
+ hr = AccessibleChildren(acc, 0, child_cnt, children, &rcv);
+ ok(hr == S_OK, "AccessibleChildren failed with %#x!\n", hr);
+
+ /*
+ * We only care about VT_DISPATCH VARIANT's here, because simple elements
+ * cannot have children, and if they were a match it would've been found
+ * in check_acc_tree_obj_for_child().
+ */
+ for (i = 0; i < child_cnt; i++)
+ {
+ VARIANT *res = &children[i];
+ IAccessible *acc_child;
+
+ if (V_VT(res) != VT_DISPATCH)
+ continue;
+
+ if (!(*found_acc))
+ {
+ hr = IDispatch_QueryInterface(V_DISPATCH(res), &IID_IAccessible, (void **)&acc_child);
+ if (SUCCEEDED(hr))
+ search_acc_tree_for_child_enumVARIANT(acc_child, child_id, found_acc, found_vid);
+
+ if (*found_acc != acc_child)
+ IAccessible_Release(acc_child);
+ }
+
+ IDispatch_Release(V_DISPATCH(res));
+ }
+
+ HeapFree(GetProcessHeap(), 0, (void *)children);
+}
+
+static void search_acc_tree_for_child_navigate(IAccessible *acc,
+ INT child_id, IAccessible **found_acc, VARIANT *found_vid)
+{
+ VARIANT vid, res;
+ HRESULT hr;
+
+ check_acc_tree_obj_for_child(acc, child_id, found_acc, found_vid);
+ if (*found_acc)
+ return;
+
+ V_VT(&vid) = VT_I4;
+ V_I4(&vid) = CHILDID_SELF;
+ hr = IAccessible_accNavigate(acc, NAVDIR_FIRSTCHILD, vid, &res);
+ if (FAILED(hr))
+ return;
+
+ while (SUCCEEDED(hr) && V_VT(&res) != VT_EMPTY && !(*found_acc))
+ {
+ switch (V_VT(&res))
+ {
+ case VT_I4:
+ vid = res;
+ hr = IAccessible_accNavigate(acc, NAVDIR_NEXT, vid, &res);
+ break;
+
+ case VT_DISPATCH:
+ {
+ IAccessible *acc_child;
+
+ hr = IDispatch_QueryInterface(V_DISPATCH(&res), &IID_IAccessible, (void **)&acc_child);
+ IDispatch_Release(V_DISPATCH(&res));
+
+ if (SUCCEEDED(hr))
+ {
+ search_acc_tree_for_child_navigate(acc_child, child_id, found_acc, found_vid);
+
+ if (!(*found_acc))
+ {
+ V_VT(&vid) = VT_I4;
+ V_I4(&vid) = CHILDID_SELF;
+ hr = IAccessible_accNavigate(acc_child, NAVDIR_NEXT, vid, &res);
+ }
+
+ if (*found_acc != acc_child)
+ IAccessible_Release(acc_child);
+ }
+ break;
+ }
+
+ default:
+ ok(0, "Invalid VARIANT type %d from accNavigate\n", V_VT(&res));
+ return;
+ }
+ }
+}
+
+HRESULT WINAPI AccTreeObject_QueryInterface(IAccessible *iface, REFIID riid, void **ppv)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+
+ if((IsEqualIID(riid, &IID_IAccessible) && !acc_query_interface_test_fail) ||
+ IsEqualIID(riid, &IID_IDispatch) ||
+ IsEqualIID(riid, &IID_IUnknown)) {
+ *ppv = iface;
+ }else if(IsEqualIID(riid, &IID_IEnumVARIANT)) {
+ *ppv = &This->IEnumVARIANT_iface;
+ }else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ IAccessible_AddRef(iface);
+ return S_OK;
+}
+
+ULONG WINAPI AccTreeObject_AddRef(IAccessible *iface)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+ return InterlockedIncrement(&This->ref);
+}
+
+ULONG WINAPI AccTreeObject_Release(IAccessible *iface)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+ return InterlockedDecrement(&This->ref);
+}
+
+HRESULT WINAPI AccTreeObject_GetTypeInfoCount(
+ IAccessible *iface, UINT *pctinfo)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_GetTypeInfo(IAccessible *iface,
+ UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_GetIDsOfNames(IAccessible *iface, REFIID riid,
+ LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_Invoke(IAccessible *iface, DISPID dispIdMember,
+ REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
+ VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accParent(
+ IAccessible *iface, IDispatch **disp_parent)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+
+ if (This->parent)
+ return IAccessible_QueryInterface(This->parent, &IID_IDispatch, (void **)disp_parent);
+ *disp_parent = NULL;
+
+ return S_FALSE;
+}
+
+HRESULT WINAPI AccTreeObject_get_accChildCount(
+ IAccessible *iface, LONG *pcountChildren)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+
+ *pcountChildren = This->info->child_count;
+
+ return S_OK;
+}
+
+HRESULT WINAPI AccTreeObject_get_accChild(IAccessible *iface,
+ VARIANT varChildID, IDispatch **ppdispChild)
+{
+ IAccessible *child;
+ AccTreeObject *obj;
+ HRESULT hr;
+
+ *ppdispChild = NULL;
+ if (!(child = get_acc_tree_obj_child(iface, varChildID)))
+ return E_INVALIDARG;
+
+ obj = impl_from_AccTreeObject(child);
+ if (obj->info->is_simple_elem)
+ {
+ IAccessible_Release(child);
+ return S_FALSE;
+ }
+
+ if (FAILED(hr = IAccessible_QueryInterface(child, &IID_IDispatch, (void **)ppdispChild)))
+ return hr;
+
+ IAccessible_Release(child);
+
+ return S_OK;
+}
+
+HRESULT WINAPI AccTreeObject_get_accName(IAccessible *iface,
+ VARIANT varID, BSTR *pszName)
+{
+ IAccessible *child;
+ AccTreeObject *obj;
+
+ *pszName = NULL;
+ if (!(child = get_acc_tree_obj_child(iface, varID)))
+ return E_INVALIDARG;
+
+ obj = impl_from_AccTreeObject(child);
+ *pszName = SysAllocString(obj->info->name);
+ IAccessible_Release(child);
+
+ return S_OK;
+}
+
+HRESULT WINAPI AccTreeObject_get_accValue(IAccessible *iface,
+ VARIANT varID, BSTR *pszValue)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accDescription(IAccessible *iface,
+ VARIANT varID, BSTR *pszDescription)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accRole(IAccessible *iface,
+ VARIANT varID, VARIANT *pvarRole)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accState(IAccessible *iface,
+ VARIANT varID, VARIANT *pvarState)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accHelp(IAccessible *iface,
+ VARIANT varID, BSTR *pszHelp)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accHelpTopic(IAccessible *iface,
+ BSTR *pszHelpFile, VARIANT varID, LONG *pidTopic)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accKeyboardShortcut(IAccessible *iface,
+ VARIANT varID, BSTR *pszKeyboardShortcut)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accFocus(IAccessible *iface, VARIANT *pvarID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accSelection(
+ IAccessible *iface, VARIANT *pvarID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_get_accDefaultAction(IAccessible *iface,
+ VARIANT varID, BSTR *pszDefaultAction)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_accSelect(IAccessible *iface,
+ LONG flagsSelect, VARIANT varID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_accLocation(IAccessible *iface, LONG *pxLeft,
+ LONG *pyTop, LONG *pcxWidth, LONG *pcyHeight, VARIANT varID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_accNavigate(IAccessible *iface,
+ LONG navDir, VARIANT varStart, VARIANT *pvarEnd)
+{
+ AccTreeObject *This = impl_from_AccTreeObject(iface);
+ AccTreeObject *obj_start, *child;
+ IAccessible *acc_start;
+ HRESULT hr = S_FALSE;
+
+ V_VT(pvarEnd) = VT_EMPTY;
+ if (!(acc_start = get_acc_tree_obj_child(iface, varStart)))
+ return E_INVALIDARG;
+
+ child = NULL;
+ obj_start = impl_from_AccTreeObject(acc_start);
+ switch (navDir)
+ {
+ case NAVDIR_FIRSTCHILD:
+ case NAVDIR_LASTCHILD:
+ {
+ UINT child_cnt = obj_start->info->child_count;
+
+ /* Cannot start with a child ID offset when getting first/last children. */
+ if ((V_I4(&varStart) == CHILDID_SELF) && child_cnt)
+ {
+ if (navDir == NAVDIR_FIRSTCHILD)
+ child = impl_from_AccTreeObject(obj_start->children[0]);
+ else
+ child = impl_from_AccTreeObject(obj_start->children[child_cnt - 1]);
+ }
+ break;
+ }
+
+ case NAVDIR_NEXT:
+ case NAVDIR_PREVIOUS:
+ {
+ AccTreeObject *parent;
+ INT next_pos;
+
+ if (!obj_start->parent)
+ break;
+
+ if (V_I4(&varStart) == CHILDID_SELF)
+ parent = impl_from_AccTreeObject(obj_start->parent);
+ else
+ parent = This;
+
+ if (navDir == NAVDIR_PREVIOUS)
+ next_pos = obj_start->info->child_pos - 1;
+ else
+ next_pos = obj_start->info->child_pos + 1;
+
+ if ((next_pos < 0) || (parent->info->child_count <= next_pos))
+ break;
+
+ child = impl_from_AccTreeObject(parent->children[next_pos]);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (child)
+ {
+ if (child->info->is_simple_elem)
+ {
+ V_VT(pvarEnd) = VT_I4;
+ V_I4(pvarEnd) = child->info->child_id;
+ }
+ else
+ {
+ V_VT(pvarEnd) = VT_DISPATCH;
+ IAccessible_QueryInterface(&child->IAccessible_iface,
+ &IID_IDispatch, (void **)&V_DISPATCH(pvarEnd));
+ }
+
+ hr = S_OK;
+ }
+
+ if (acc_start)
+ IAccessible_Release(acc_start);
+
+ return hr;
+}
+
+HRESULT WINAPI AccTreeObject_accHitTest(IAccessible *iface,
+ LONG xLeft, LONG yTop, VARIANT *pvarID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_accDoDefaultAction(
+ IAccessible *iface, VARIANT varID)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_put_accName(IAccessible *iface,
+ VARIANT varID, BSTR pszName)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AccTreeObject_put_accValue(IAccessible *iface,
+ VARIANT varID, BSTR pszValue)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+IAccessibleVtbl AccTreeObjAccessibleVtbl = {
+ AccTreeObject_QueryInterface,
+ AccTreeObject_AddRef,
+ AccTreeObject_Release,
+ AccTreeObject_GetTypeInfoCount,
+ AccTreeObject_GetTypeInfo,
+ AccTreeObject_GetIDsOfNames,
+ AccTreeObject_Invoke,
+ AccTreeObject_get_accParent,
+ AccTreeObject_get_accChildCount,
+ AccTreeObject_get_accChild,
+ AccTreeObject_get_accName,
+ AccTreeObject_get_accValue,
+ AccTreeObject_get_accDescription,
+ AccTreeObject_get_accRole,
+ AccTreeObject_get_accState,
+ AccTreeObject_get_accHelp,
+ AccTreeObject_get_accHelpTopic,
+ AccTreeObject_get_accKeyboardShortcut,
+ AccTreeObject_get_accFocus,
+ AccTreeObject_get_accSelection,
+ AccTreeObject_get_accDefaultAction,
+ AccTreeObject_accSelect,
+ AccTreeObject_accLocation,
+ AccTreeObject_accNavigate,
+ AccTreeObject_accHitTest,
+ AccTreeObject_accDoDefaultAction,
+ AccTreeObject_put_accName,
+ AccTreeObject_put_accValue
+};
+
+/*
+ * AccTreeObj enumVARIANT Vtbl.
+ */
+inline AccTreeObject* impl_from_AccTreeObject_EnumVARIANT(IEnumVARIANT *iface)
+{
+ return CONTAINING_RECORD(iface, AccTreeObject, IEnumVARIANT_iface);
+}
+
+HRESULT WINAPI AccTreeObject_EnumVARIANT_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+ return IAccessible_QueryInterface(&This->IAccessible_iface, riid, ppv);
+}
+
+ULONG WINAPI AccTreeObject_EnumVARIANT_AddRef(IEnumVARIANT *iface)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+ return IAccessible_AddRef(&This->IAccessible_iface);
+}
+
+ULONG WINAPI AccTreeObject_EnumVARIANT_Release(IEnumVARIANT *iface)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+ return IAccessible_Release(&This->IAccessible_iface);
+}
+
+HRESULT WINAPI AccTreeObject_EnumVARIANT_Next(IEnumVARIANT *iface,
+ ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+ ULONG fetched = 0;
+ UINT i;
+
+ for (i = This->enum_pos; i < This->info->child_count; i++)
+ {
+ AccTreeObject *child = impl_from_AccTreeObject(This->children[i]);
+
+ if (child->info->is_simple_elem)
+ {
+ V_VT(&rgVar[i]) = VT_I4;
+ V_I4(&rgVar[i]) = child->info->child_id;
+ }
+ else
+ {
+ V_VT(&rgVar[i]) = VT_DISPATCH;
+ IAccessible_QueryInterface(&child->IAccessible_iface, &IID_IDispatch,
+ (void **)&V_DISPATCH(&rgVar[i]));
+ }
+
+ fetched++;
+ }
+
+ *pCeltFetched = fetched;
+
+ return celt == fetched ? S_OK : S_FALSE;
+}
+
+HRESULT WINAPI AccTreeObject_EnumVARIANT_Skip(IEnumVARIANT *iface, ULONG celt)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+
+ if ((celt + This->enum_pos) < This->info->child_count)
+ {
+ This->enum_pos += celt;
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+HRESULT WINAPI AccTreeObject_EnumVARIANT_Reset(IEnumVARIANT *iface)
+{
+ AccTreeObject *This = impl_from_AccTreeObject_EnumVARIANT(iface);
+
+ This->enum_pos = 0;
+
+ return S_OK;
+}
+
+HRESULT WINAPI AccTreeObject_EnumVARIANT_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+const IEnumVARIANTVtbl AccTreeObjEnumVARIANTVtbl = {
+ AccTreeObject_EnumVARIANT_QueryInterface,
+ AccTreeObject_EnumVARIANT_AddRef,
+ AccTreeObject_EnumVARIANT_Release,
+ AccTreeObject_EnumVARIANT_Next,
+ AccTreeObject_EnumVARIANT_Skip,
+ AccTreeObject_EnumVARIANT_Reset,
+ AccTreeObject_EnumVARIANT_Clone
+};
+
+static HRESULT create_acc_obj_tree(const acc_tree_object_info *info,
+ unsigned int count)
+{
+ AccTreeObject *tree_objs;
+ unsigned int i;
+
+ object_tree = NULL;
+
+ tree_objs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof(*tree_objs) * count);
+ if (!tree_objs)
+ return E_OUTOFMEMORY;
+
+ for (i = 0; i < count; i++)
+ {
+ const acc_tree_object_info *cur_obj = &info[i];
+ AccTreeObject *obj = &tree_objs[i];
+
+ obj->IAccessible_iface.lpVtbl = &AccTreeObjAccessibleVtbl;
+ obj->IEnumVARIANT_iface.lpVtbl = &AccTreeObjEnumVARIANTVtbl;
+ obj->ref = 1;
+ obj->info = cur_obj;
+
+ if (cur_obj->parent_id != ACC_TREE_OBJ_ID_NONE)
+ {
+ AccTreeObject *parent = &tree_objs[cur_obj->parent_id - 1];
+
+ obj->parent = &parent->IAccessible_iface;
+ parent->children[cur_obj->child_pos] = &obj->IAccessible_iface;
+ }
+ }
+
+ object_tree = tree_objs;
+
+ return S_OK;
+}
+
+static void free_acc_obj_tree(unsigned int count)
+{
+ unsigned int i;
+
+ if (!object_tree)
+ return;
+
+ for (i = 0; i < count; i++)
+ IAccessible_Release(&object_tree[i].IAccessible_iface);
+
+ HeapFree(GetProcessHeap(), 0, object_tree);
+ object_tree = NULL;
+}
+
static void test_getroletext(void)
{
INT ret, role;
@@ -564,7 +1330,13 @@ static LRESULT WINAPI test_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
if(lparam == (DWORD)OBJID_CURSOR)
return E_UNEXPECTED;
if(lparam == (DWORD)OBJID_CLIENT)
+ {
+ if (object_tree)
+ return LresultFromObject(&IID_IAccessible, wparam,
+ (IUnknown *)&object_tree->IAccessible_iface);
+
return LresultFromObject(&IID_IUnknown, wparam, &Object);
+ }
if(lparam == (DWORD)OBJID_WINDOW)
return 0;
@@ -621,6 +1393,227 @@ static void test_AccessibleObjectFromWindow(void)
DestroyWindow(hwnd);
}
+static void test_AccessibleObjectFromEvent(void)
+{
+ IAccessible *acc, *acc_child;
+ const WCHAR *expected_name;
+ BSTR obj_name;
+ VARIANT cid;
+ HRESULT hr;
+ HWND hwnd;
+
+ hwnd = CreateWindowA("oleacc_test", "test", WS_OVERLAPPEDWINDOW,
+ 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+ ok(hwnd != NULL, "CreateWindow failed\n");
+
+ hr = create_acc_obj_tree(acc_from_event_obj_tree,
+ ARRAY_SIZE(acc_from_event_obj_tree));
+ ok(hr == S_OK, "failed to create accessible object tree, hr %#x\n", hr);
+
+ hr = AccessibleObjectFromEvent(NULL, OBJID_CLIENT, CHILDID_SELF, &acc, &cid);
+ ok(hr == E_FAIL, "got %#x\n", hr);
+
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, CHILDID_SELF, NULL, &cid);
+ ok(hr == E_INVALIDARG, "got %#x\n", hr);
+
+ /*
+ * Test get_accChild's IDispatch::QueryInterface failing to return an
+ * IAccessible interface.
+ */
+ acc_query_interface_test_fail = TRUE;
+ acc = NULL;
+ VariantInit(&cid);
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, CHILDID_SELF, &acc, &cid);
+ ok(hr == E_NOINTERFACE, "got %#x\n", hr);
+ ok(acc == NULL, "Unexpected acc %p\n", acc);
+ ok(V_VT(&cid) == VT_EMPTY, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ acc_query_interface_test_fail = FALSE;
+
+ /* Same behavior as AccessibleObjectFromWindow. */
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CURSOR, CHILDID_SELF, &acc, &cid);
+ ok(hr == E_UNEXPECTED, "got %#x\n", hr);
+
+ /* Get a full object child. */
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_TREE_OBJ_ID_CHILD_0, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == CHILDID_SELF, "got %#x, expected %#x\n", V_I4(&cid), CHILDID_SELF);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_0 - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc);
+
+ /* Get a 'simple element' child. */
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_TREE_OBJ_ID_CHILD_2, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == ACC_TREE_OBJ_ID_CHILD_2, "got %#x, expected %#x\n", V_I4(&cid), ACC_TREE_OBJ_ID_CHILD_2);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_2 - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+
+ /*
+ * Make sure the IAccessible returned for a simple element is the
+ * root IAccessible.
+ */
+ V_I4(&cid) = CHILDID_SELF;
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_ROOT - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc);
+
+ /*
+ * Invalid child ID's are treated by AccessibleObjectFromEvent as simple
+ * elements, regardless of if get_accChild returns a failure code instead
+ * of S_FALSE to indicate a simple element. The IAccessible returned is
+ * the root object, and child ID is still set to the invalid child ID.
+ */
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_TREE_OBJ_ID_CHILD_2 + 1, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == (ACC_TREE_OBJ_ID_CHILD_2 + 1), "got %#x, expected %#x\n",
+ V_I4(&cid), ACC_TREE_OBJ_ID_CHILD_2 + 1);
+
+ V_VT(&cid) = VT_I4;
+ V_I4(&cid) = CHILDID_SELF;
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_ROOT - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc);
+
+ /*
+ * AccessibleObjectFromEvent will not search an object tree for a child,
+ * so if a given child ID is not returned by the get_accChild method on the
+ * root IAccessible, it won't be found by AccessibleObjectFromEvent. This
+ * is an issue in things such as web browsers, where one HWND is used as a
+ * root object with a complex tree of objects within it.
+ */
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_TREE_OBJ_ID_CHILD_1_0, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == (ACC_TREE_OBJ_ID_CHILD_1_0), "got %#x, expected %#x\n",
+ V_I4(&cid), ACC_TREE_OBJ_ID_CHILD_1_0);
+
+ V_VT(&cid) = VT_I4;
+ V_I4(&cid) = CHILDID_SELF;
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_ROOT - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc);
+
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_TREE_OBJ_ID_CHILD_1_1, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == (ACC_TREE_OBJ_ID_CHILD_1_1), "got %#x, expected %#x\n",
+ V_I4(&cid), ACC_TREE_OBJ_ID_CHILD_1_1);
+
+ V_VT(&cid) = VT_I4;
+ V_I4(&cid) = CHILDID_SELF;
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_ROOT - 1].name;
+ hr = IAccessible_get_accName(acc, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc);
+
+ /*
+ * Show that ACC_TREE_OBJ_ID_CHILD_1's children are actually reachable
+ * from the root IAccessible using both possible methods of traversing
+ * the IAccessible tree (accNavigate and AccessibleChildren).
+ */
+ hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, CHILDID_SELF, &acc, &cid);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(V_VT(&cid) == VT_I4, "got %#x, expected %#x\n", V_VT(&cid), VT_I4);
+ ok(V_I4(&cid) == CHILDID_SELF, "got %#x, expected %#x\n", V_I4(&cid), CHILDID_SELF);
+
+ /* IAccessible_accNavigate method. */
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ acc_child = NULL;
+ search_acc_tree_for_child_navigate(acc, ACC_TREE_OBJ_ID_CHILD_1_0, &acc_child, &cid);
+ ok(!!acc_child, "Failed to find child id %#x in tree!\n", ACC_TREE_OBJ_ID_CHILD_1_0);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_1_0 - 1].name;
+ hr = IAccessible_get_accName(acc_child, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc_child);
+
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ acc_child = NULL;
+ search_acc_tree_for_child_navigate(acc, ACC_TREE_OBJ_ID_CHILD_1_1, &acc_child, &cid);
+ ok(!!acc_child, "Failed to find child id %#x in tree!\n", ACC_TREE_OBJ_ID_CHILD_1_1);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_1_1 - 1].name;
+ hr = IAccessible_get_accName(acc_child, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc_child);
+
+ /* AccessibleChildren (IEnumVARIANT) method. */
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ acc_child = NULL;
+ search_acc_tree_for_child_enumVARIANT(acc, ACC_TREE_OBJ_ID_CHILD_1_0, &acc_child, &cid);
+ ok(!!acc_child, "Failed to find child id %#x in tree!\n", ACC_TREE_OBJ_ID_CHILD_1_0);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_1_0 - 1].name;
+ hr = IAccessible_get_accName(acc_child, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc_child);
+
+ V_I4(&cid) = 0;
+ V_VT(&cid) = VT_EMPTY;
+ acc_child = NULL;
+ search_acc_tree_for_child_enumVARIANT(acc, ACC_TREE_OBJ_ID_CHILD_1_1, &acc_child, &cid);
+ ok(!!acc_child, "Failed to find child id %#x in tree!\n", ACC_TREE_OBJ_ID_CHILD_1_1);
+
+ expected_name = acc_from_event_obj_tree[ACC_TREE_OBJ_ID_CHILD_1_1 - 1].name;
+ hr = IAccessible_get_accName(acc_child, cid, &obj_name);
+ ok(SUCCEEDED(hr), "got %#x\n", hr);
+ ok(!lstrcmpW(obj_name, expected_name), "expected %s, got %s\n",
+ wine_dbgstr_w(expected_name), wine_dbgstr_w(obj_name));
+ SysFreeString(obj_name);
+ IAccessible_Release(acc_child);
+
+ IAccessible_Release(acc);
+
+ DestroyWindow(hwnd);
+ free_acc_obj_tree(ARRAY_SIZE(acc_from_event_obj_tree));
+}
+
static void test_GetProcessHandleFromHwnd(void)
{
HANDLE proc;
@@ -1061,6 +2054,7 @@ START_TEST(main)
test_GetProcessHandleFromHwnd();
test_default_client_accessible_object();
test_AccessibleChildren(&Accessible);
+ test_AccessibleObjectFromEvent();
unregister_window_class();
CoUninitialize();
--
2.25.1