[PATCH v4 2/2] oleacc: Add tests for AccessibleObjectFromEvent.
Piotr Caban
piotr.caban at gmail.com
Mon Aug 30 12:11:16 CDT 2021
Hi Connor,
On 8/30/21 5:27 PM, Connor McAdams wrote:
> +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;
You can just define it as:
IAccessible *children[3];
and avoid allocations/frees.
> +} AccTreeObject;
> +
> +AccTreeObject *object_tree = NULL;
> +BOOL acc_query_interface_test_fail = FALSE;
static
> +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)
> + 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;
Even so it's internal function it's good to update refcount:
IAccessible_AddRef(acc);
There's lot's of places that are hard to follow without it.
> +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);
> + }
> + }
> +}
The function depends on found_acc and found_vid to be initialized to
handle get_accChild errors. While it's a helper it's still not the best
design. You can also avoid indentation level by returning early on
get_accChild failure.
> + children = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
> + sizeof(*children) * child_cnt);
> + hr = AccessibleChildren(acc, 0, child_cnt, children, &rcv);
> + ok(SUCCEEDED(hr), "AccessibleChildren failed with %#x!\n", hr);
It would be better to check exact hr value:
ok(hr == S_OK, ...
> +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;
> + }
> +
> + /*
> + * Shouldn't ever reach here, if type isn't VT_I4 or VT_DISPATCH,
> + * we've got a problem.
> + */
> + default:
> + return;
It's better to remove the comment and add something like:
ok(0, ...
> +HRESULT WINAPI AccTreeObject_GetTypeInfoCount(
> + IAccessible *iface, UINT *pctinfo)
> +{
> + *pctinfo = 0;
> +
> + return S_OK;
> +}
The function is not called. It's better to do something like:
ok(0, "unexpected call\n");
return E_NOTIMPL;
The same applies to other IDispatch methods (the ok() call may be even
skipped).
> +HRESULT WINAPI AccTreeObject_get_accParent(
> + IAccessible *iface, IDispatch **ppdispParent)
> +{
> + AccTreeObject *This = impl_from_AccTreeObject(iface);
> +
> + *ppdispParent = NULL;
> + if (This->parent)
> + {
> + IAccessible_QueryInterface(This->parent, &IID_IDispatch, (void **)ppdispParent);
> + return S_OK;
> + }
> +
> + return S_FALSE;
There's a '\t' before return S_OK. I would implement the function in
following way:
if (This->parent)
return IAccessible_QueryInterface(...);
*ppdispParent = NULL;
return S_FALSE;
It would be also nice to change the ppdispParent argument name.
>
> +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));
> + if (FAILED(hr))
> + {
> + trace("Failed to create accessible object tree, hr %#x, aborting test.\n", hr);
> + DestroyWindow(hwnd);
> + return;
> + }
Are you ever expecting it to fail? A simple ok(hr == S_OK, ...) should
be enough.
Thanks,
Piotr
More information about the wine-devel
mailing list