terminal/src/interactivity/win32/windowUiaProvider.cpp
Dustin L. Howett 9ce884c4fb
Tie up some A11y loose threads (#6417)
This pull request moves WindowUiaProvider back into Win32 interactivity
and deletes all mention of it from Windows Terminal. Terminal does not
have a single toplevel window that requires Console-like UIA, as each
Xaml control inside it is in charge of its own destiny.

I've also merged `IUiaWindow` and `IConsoleWindow` back together, as
well as `WindowUiaProviderBase` and `WindowUiaProvider`.

Things look a lot more like they did before we tore them apart.

## PR Checklist
* [x] Closes #3564
* [x] CLA
* [x] Tests added/passed (manual)
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already

## Validation

Carlos validated conhost and terminal on this branch.
2020-06-10 15:15:26 +00:00

311 lines
8.9 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "screenInfoUiaProvider.hpp"
#include "windowUiaProvider.hpp"
#include "../types/IUiaData.h"
#include "../host/renderData.hpp"
#include "../inc/ServiceLocator.hpp"
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity::Win32;
HRESULT WindowUiaProvider::RuntimeClassInitialize(_In_ IConsoleWindow* baseWindow) noexcept
try
{
_baseWindow = baseWindow;
Globals& g = ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();
IUiaData* uiaData = &gci.renderData;
RETURN_IF_FAILED(WRL::MakeAndInitialize<ScreenInfoUiaProvider>(&_pScreenInfoProvider, uiaData, this));
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(pWindowProvider, ApiCall::Create, nullptr);
return S_OK;
}
CATCH_RETURN();
[[nodiscard]] HRESULT WindowUiaProvider::Signal(_In_ EVENTID id)
{
HRESULT hr = S_OK;
// ScreenInfoUiaProvider is responsible for signaling selection
// changed events and text changed events
if (id == UIA_Text_TextSelectionChangedEventId ||
id == UIA_Text_TextChangedEventId)
{
if (_pScreenInfoProvider)
{
hr = _pScreenInfoProvider->Signal(id);
}
else
{
hr = E_POINTER;
}
return hr;
}
if (_signalEventFiring.find(id) != _signalEventFiring.end() &&
_signalEventFiring[id] == true)
{
return hr;
}
try
{
_signalEventFiring[id] = true;
}
CATCH_RETURN();
hr = UiaRaiseAutomationEvent(this, id);
_signalEventFiring[id] = false;
return hr;
}
[[nodiscard]] HRESULT WindowUiaProvider::SetTextAreaFocus()
{
try
{
return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId);
}
CATCH_RETURN();
}
#pragma region IRawElementProviderSimple
// Implementation of IRawElementProviderSimple::get_ProviderOptions.
// Gets UI Automation provider options.
IFACEMETHODIMP WindowUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* pOptions)
{
RETURN_HR_IF_NULL(E_INVALIDARG, pOptions);
RETURN_IF_FAILED(_EnsureValidHwnd());
*pOptions = ProviderOptions_ServerSideProvider;
return S_OK;
}
// Implementation of IRawElementProviderSimple::get_PatternProvider.
// Gets the object that supports ISelectionPattern.
IFACEMETHODIMP WindowUiaProvider::GetPatternProvider(_In_ PATTERNID /*patternId*/,
_COM_Outptr_result_maybenull_ IUnknown** ppInterface)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface);
*ppInterface = nullptr;
RETURN_IF_FAILED(_EnsureValidHwnd());
return S_OK;
}
// Implementation of IRawElementProviderSimple::get_PropertyValue.
// Gets custom properties.
IFACEMETHODIMP WindowUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyId, _Out_ VARIANT* pVariant)
{
RETURN_HR_IF_NULL(E_INVALIDARG, pVariant);
RETURN_IF_FAILED(_EnsureValidHwnd());
pVariant->vt = VT_EMPTY;
// Returning the default will leave the property as the default
// so we only really need to touch it for the properties we want to implement
if (propertyId == UIA_ControlTypePropertyId)
{
pVariant->vt = VT_I4;
pVariant->lVal = UIA_WindowControlTypeId;
}
else if (propertyId == UIA_AutomationIdPropertyId)
{
pVariant->bstrVal = SysAllocString(AutomationIdPropertyName);
if (pVariant->bstrVal != nullptr)
{
pVariant->vt = VT_BSTR;
}
}
else if (propertyId == UIA_IsControlElementPropertyId)
{
pVariant->vt = VT_BOOL;
pVariant->boolVal = VARIANT_TRUE;
}
else if (propertyId == UIA_IsContentElementPropertyId)
{
pVariant->vt = VT_BOOL;
pVariant->boolVal = VARIANT_TRUE;
}
else if (propertyId == UIA_IsKeyboardFocusablePropertyId)
{
pVariant->vt = VT_BOOL;
pVariant->boolVal = VARIANT_TRUE;
}
else if (propertyId == UIA_HasKeyboardFocusPropertyId)
{
pVariant->vt = VT_BOOL;
pVariant->boolVal = VARIANT_TRUE;
}
else if (propertyId == UIA_ProviderDescriptionPropertyId)
{
pVariant->bstrVal = SysAllocString(ProviderDescriptionPropertyName);
if (pVariant->bstrVal != nullptr)
{
pVariant->vt = VT_BSTR;
}
}
return S_OK;
}
// Implementation of IRawElementProviderSimple::get_HostRawElementProvider.
// Gets the default UI Automation provider for the host window. This provider
// supplies many properties.
IFACEMETHODIMP WindowUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
try
{
const HWND hwnd = GetWindowHandle();
return UiaHostProviderFromHwnd(hwnd, ppProvider);
}
catch (...)
{
return gsl::narrow_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE);
}
}
#pragma endregion
#pragma region IRawElementProviderFragment
IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
*ppProvider = nullptr;
HRESULT hr = S_OK;
if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild)
{
RETURN_IF_FAILED(_pScreenInfoProvider.CopyTo(ppProvider));
// signal that the focus changed
LOG_IF_FAILED(_pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId));
}
// For the other directions (parent, next, previous) the default of nullptr is correct
return hr;
}
IFACEMETHODIMP WindowUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppRuntimeId);
RETURN_IF_FAILED(_EnsureValidHwnd());
// Root defers this to host, others must implement it...
*ppRuntimeId = nullptr;
return S_OK;
}
IFACEMETHODIMP WindowUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect)
{
RETURN_HR_IF_NULL(E_INVALIDARG, pRect);
RETURN_IF_FAILED(_EnsureValidHwnd());
RETURN_HR_IF_NULL((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, _baseWindow);
RECT const rc = _baseWindow->GetWindowRect();
pRect->left = rc.left;
pRect->top = rc.top;
LONG longWidth = 0;
RETURN_IF_FAILED(LongSub(rc.right, rc.left, &longWidth));
pRect->width = longWidth;
LONG longHeight = 0;
RETURN_IF_FAILED(LongSub(rc.bottom, rc.top, &longHeight));
pRect->height = longHeight;
return S_OK;
}
IFACEMETHODIMP WindowUiaProvider::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppRoots);
RETURN_IF_FAILED(_EnsureValidHwnd());
*ppRoots = nullptr;
return S_OK;
}
IFACEMETHODIMP WindowUiaProvider::SetFocus()
{
RETURN_IF_FAILED(_EnsureValidHwnd());
return Signal(UIA_AutomationFocusChangedEventId);
}
IFACEMETHODIMP WindowUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
RETURN_IF_FAILED(_EnsureValidHwnd());
RETURN_IF_FAILED(QueryInterface(IID_PPV_ARGS(ppProvider)));
return S_OK;
}
#pragma endregion
#pragma region IRawElementProviderFragmentRoot
IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/,
_In_ double /*y*/,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
RETURN_IF_FAILED(_pScreenInfoProvider.CopyTo(ppProvider));
return S_OK;
}
IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
return _pScreenInfoProvider->QueryInterface(IID_PPV_ARGS(ppProvider));
}
#pragma endregion
HWND WindowUiaProvider::GetWindowHandle() const
{
if (_baseWindow)
{
return _baseWindow->GetWindowHandle();
}
else
{
return nullptr;
}
}
[[nodiscard]] HRESULT WindowUiaProvider::_EnsureValidHwnd() const
{
try
{
HWND const hwnd = GetWindowHandle();
RETURN_HR_IF((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, !(IsWindow(hwnd)));
}
CATCH_RETURN();
return S_OK;
}
void WindowUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
{
_baseWindow->ChangeViewport(NewWindow);
}
RECT WindowUiaProvider::GetWindowRect() const noexcept
{
return _baseWindow->GetWindowRect();
}