From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 119 +++++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 39 +++++++ 2 files changed, 158 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ce3ee303fd1..ee0eb312642 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -3382,7 +3382,10 @@ static void test_uia_prov_from_acc_navigation(void)
static void test_uia_prov_from_acc_properties(void) { + BOOL is_win10_1809_or_above = FALSE; IRawElementProviderSimple *elprov; + RECT rect[2] = { 0 }; + HWND hwnd, hwnd2; HRESULT hr; VARIANT v; int i, x; @@ -3441,6 +3444,8 @@ static void test_uia_prov_from_acc_properties(void) ok(check_variant_i4(&v, UIA_ButtonControlTypeId) || broken(V_VT(&v) == VT_EMPTY), /* Windows < 10 1809 */ "MSAA role %d: V_I4(&v) = %ld\n", Accessible.role, V_I4(&v)); CHECK_CALLED(Accessible_get_accRole); + if (V_VT(&v) == VT_I4) + is_win10_1809_or_above = TRUE;
if (V_VT(&v) == VT_EMPTY) SET_EXPECT(Accessible_get_accRole); @@ -3506,6 +3511,120 @@ static void test_uia_prov_from_acc_properties(void)
IRawElementProviderSimple_Release(elprov); ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + /* + * UIA_IsOffscreenPropertyId relies upon either STATE_SYSTEM_OFFSCREEN + * being set, or accLocation returning a location that is within the + * client area bounding box of the HWND it is contained within. + */ + hwnd = CreateWindowA("UiaProviderFromIAccessible class", "Test window 2", WS_OVERLAPPEDWINDOW, + 0, 0, 100, 100, NULL, NULL, NULL, NULL); + + hwnd2 = Accessible.ow_hwnd; + Accessible.ow_hwnd = hwnd; + hr = pUiaProviderFromIAccessible(&Accessible.IAccessible_iface, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref); + + set_accessible_props(&Accessible, 0, STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", 0, 0, 0, 0); + SET_EXPECT(Accessible_get_accState); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + + /* accLocation fails, will return FALSE. */ + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", 0, 0, 0, 0); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Window is visible, Accessible is within its bounds. */ + ShowWindow(Accessible.ow_hwnd, SW_SHOW); + ok(GetClientRect(Accessible.ow_hwnd, &rect[0]), "GetClientRect returned FALSE\n"); + MapWindowPoints(Accessible.ow_hwnd, NULL, (POINT *)&rect[0], 2); + + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].left, rect[0].top, + (rect[0].right - rect[0].left), (rect[0].bottom - rect[0].top)); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "Unexpected VT %d\n", V_VT(&v)); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* + * Window is invisible, Accessible is within its bounds. Window visibility + * doesn't effect whether or not an IAccessible is considered offscreen. + */ + ShowWindow(Accessible.ow_hwnd, SW_HIDE); + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].left, rect[0].top, + (rect[0].right - rect[0].left), (rect[0].bottom - rect[0].top)); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "Unexpected VT %d\n", V_VT(&v)); + ok(check_variant_bool(&v, FALSE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Accessible now outside of its window's bounds. */ + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[0].right, rect[0].bottom, + 10, 10); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Accessible within window bounds, but not client area bounds. */ + ok(GetWindowRect(Accessible.ow_hwnd, &rect[1]), "GetClientRect returned FALSE\n"); + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[1].left, rect[1].top, + rect[0].left - 1, rect[0].top - 1); + + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE), "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + /* Accessible within window bounds, but window is now destroyed. */ + DestroyWindow(hwnd); + set_accessible_props(&Accessible, 0, ~STATE_SYSTEM_OFFSCREEN, 0, L"Accessible", rect[1].left, rect[1].top, + rect[1].right, rect[1].bottom); + + VariantClear(&v); + SET_EXPECT(Accessible_get_accState); + SET_EXPECT(Accessible_accLocation); + hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_IsOffscreenPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BOOL, "V_VT(&v) = %d\n", V_VT(&v)); + ok(check_variant_bool(&v, TRUE) || /* Win10v1809 32-bit returns FALSE here for some reason. */ + broken(is_win10_1809_or_above && (sizeof(void *) == 4) && check_variant_bool(&v, FALSE)), + "Unexpected BOOL %#x\n", V_BOOL(&v)); + CHECK_CALLED(Accessible_get_accState); + CHECK_CALLED(Accessible_accLocation); + + IRawElementProviderSimple_Release(elprov); + ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref); + + DestroyWindow(hwnd); + Accessible.ow_hwnd = hwnd2; }
static void test_UiaProviderFromIAccessible(void) diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index a7d41248abc..119a7e69abc 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -676,6 +676,45 @@ HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface, break; }
+ case UIA_IsOffscreenPropertyId: + { + RECT rect[2] = { 0 }; + RECT intersect_rect; + LONG width, height; + + variant_init_bool(ret_val, FALSE); + if (msaa_check_acc_state(msaa_prov->acc, msaa_prov->cid, STATE_SYSTEM_OFFSCREEN)) + { + variant_init_bool(ret_val, TRUE); + break; + } + + hr = IAccessible_accLocation(msaa_prov->acc, &rect[0].left, &rect[0].top, &width, &height, msaa_prov->cid); + if (FAILED(hr)) + break; + + rect[0].right = rect[0].left + width; + rect[0].bottom = rect[0].top + height; + SetLastError(NOERROR); + if (!GetClientRect(msaa_prov->hwnd, &rect[1])) + { + if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) + variant_init_bool(ret_val, TRUE); + break; + } + + SetLastError(NOERROR); + if (!MapWindowPoints(msaa_prov->hwnd, NULL, (POINT *)&rect[1], 2) && GetLastError()) + { + if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) + variant_init_bool(ret_val, TRUE); + break; + } + + variant_init_bool(ret_val, !IntersectRect(&intersect_rect, &rect[0], &rect[1])); + break; + } + default: FIXME("Unimplemented propertyId %d\n", prop_id); break;