Skip to content

Commit

Permalink
UIA: Fix GetVisibleRanges() and add Tracing (#4495)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
Debugging our custom UIA providers has been a painful experience because outputting content to VS may result in UIA Clients getting impatient and giving up on extracting data.

Adding tracing allows us to debug these providers without getting in the way of reproducing a bug. This will help immensely with developing accessibility features on Windows Terminal and Console.

This pull request additionally contains payload from #4526:
* Make GetVisibleRanges() return one range (and add tracing for it).
`ScreenInfoUiaProvider::GetVisibleRanges()` used to return one range per line of visible text. The documentation for this function says that we should return one per contiguous span of text. Since all of the text in the TermControl will always be contiguous (at least by our standards), we should only ever be returning one range.

## PR Checklist
* [x] Closes #1914. Closes #4507.
* [x] CLA signed

## Detailed Description of the Pull Request / Additional comments
`UiaTracing` is a singleton class that is in charge of registration for trace logging. `TextRange` is used to trace `UiaTextRange`, whereas `TextProvider` is used to trace `ScreenInfoUiaProviderBase`.

`_getValue()` is overloaded to transform complex objects and enums into a string for logging.

`_getTextValue()` had to be added to be able to trace the text a UiaTextRange included. This makes following UiaTextRanges much simpler.

## Validation Steps Performed
Performed a few operations when under NVDA/Narrator and manually checked the results.
  • Loading branch information
carlos-zamora authored Feb 20, 2020
1 parent 215df32 commit 360c655
Show file tree
Hide file tree
Showing 11 changed files with 780 additions and 1,237 deletions.
2 changes: 1 addition & 1 deletion src/host/renderData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#pragma hdrstop

using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity::Win32;
using namespace Microsoft::Console::Interactivity;
using Microsoft::Console::Interactivity::ServiceLocator;

#pragma region IBaseData
Expand Down
716 changes: 0 additions & 716 deletions src/host/tracing.cpp

Large diffs are not rendered by default.

39 changes: 0 additions & 39 deletions src/host/tracing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,6 @@ Author(s):

#include "../types/inc/Viewport.hpp"

namespace Microsoft::Console::Interactivity::Win32
{
class UiaTextRange;

namespace UiaTextRangeTracing
{
enum class ApiCall;
struct IApiMsg;
}

class ScreenInfoUiaProvider;

namespace ScreenInfoUiaProviderTracing
{
enum class ApiCall;
struct IApiMsg;
}
}

#if DBG
#define DBGCHARS(_params_) \
{ \
Expand Down Expand Up @@ -83,30 +64,10 @@ class Tracing

static void __stdcall TraceFailure(const wil::FailureInfo& failure) noexcept;

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
#if 0
static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::UiaTextRange* const range,
const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::ApiCall apiCall,
const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::IApiMsg* const apiMsg);

static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProvider* const pProvider,
const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::ApiCall apiCall,
const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::IApiMsg* const apiMsg);

static void s_TraceUia(const Microsoft::Console::Types::WindowUiaProvider* const pProvider,
const Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall apiCall,
const Microsoft::Console::Types::WindowUiaProviderTracing::IApiMsg* const apiMsg);
#endif

private:
static ULONG s_ulDebugFlag;

Tracing(std::function<void()> onExit);

std::function<void()> _onExit;

static const wchar_t* const _textPatternRangeEndpointToString(int endpoint);
static const wchar_t* const _textUnitToString(int unit);
static const wchar_t* const _eventIdToString(long eventId);
static const wchar_t* const _directionToString(int direction);
};
125 changes: 38 additions & 87 deletions src/types/ScreenInfoUiaProviderBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

#include "ScreenInfoUiaProviderBase.h"
#include "WindowUiaProviderBase.hpp"
#include "UiaTracing.h"

using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing;

// A helper function to create a SafeArray Version of an int array of a specified length
SAFEARRAY* BuildIntSafeArray(std::basic_string_view<int> data)
Expand Down Expand Up @@ -38,6 +38,7 @@ try
RETURN_HR_IF_NULL(E_INVALIDARG, pData);
_pData = pData;
_wordDelimiters = wordDelimiters;
UiaTracing::TextProvider::Constructor(*this);
return S_OK;
}
CATCH_RETURN();
Expand All @@ -62,11 +63,6 @@ CATCH_RETURN();
hr = UiaRaiseAutomationEvent(pProvider, id);
_signalFiringMapping[id] = false;

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
// tracing
/*ApiMsgSignal apiMsg;
apiMsg.Signal = id;
Tracing::s_TraceUia(this, ApiCall::Signal, &apiMsg);*/
return hr;
}

Expand All @@ -78,9 +74,8 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_ProviderOptions(_Out_ ProviderOpti
{
RETURN_HR_IF_NULL(E_INVALIDARG, pOptions);

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr);
*pOptions = ProviderOptions_ServerSideProvider;
UiaTracing::TextProvider::get_ProviderOptions(*this, *pOptions);
return S_OK;
}

Expand All @@ -92,12 +87,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patt
RETURN_HR_IF(E_INVALIDARG, ppInterface == nullptr);
*ppInterface = nullptr;

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetPatternProvider, nullptr);

*ppInterface = nullptr;
HRESULT hr = S_OK;

if (patternId == UIA_TextPatternId)
{
hr = QueryInterface(IID_PPV_ARGS(ppInterface));
Expand All @@ -106,6 +96,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patt
*ppInterface = nullptr;
}
}
UiaTracing::TextProvider::GetPatternProvider(*this, patternId);
return hr;
}

Expand All @@ -114,9 +105,6 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patt
IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyId,
_Out_ VARIANT* pVariant) noexcept
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr);

pVariant->vt = VT_EMPTY;

// Returning the default will leave the property as the default
Expand Down Expand Up @@ -179,16 +167,15 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPropertyValue(_In_ PROPERTYID prope
pVariant->boolVal = VARIANT_TRUE;
}

UiaTracing::TextProvider::GetPropertyValue(*this, propertyId);
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr);
RETURN_HR_IF(E_INVALIDARG, ppProvider == nullptr);
*ppProvider = nullptr;

UiaTracing::TextProvider::get_HostRawElementProvider(*this);
return S_OK;
}
#pragma endregion
Expand All @@ -197,9 +184,6 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr

IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr);

// Root defers this to host, others must implement it...
RETURN_HR_IF(E_INVALIDARG, ppRuntimeId == nullptr);
*ppRuntimeId = nullptr;
Expand All @@ -212,24 +196,21 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_
*ppRuntimeId = BuildIntSafeArray(span);
RETURN_IF_NULL_ALLOC(*ppRuntimeId);

UiaTracing::TextProvider::GetRuntimeId(*this);
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) noexcept
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr);

RETURN_HR_IF(E_INVALIDARG, ppRoots == nullptr);
*ppRoots = nullptr;
UiaTracing::TextProvider::GetEmbeddedFragmentRoots(*this);
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::SetFocus()
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::SetFocus, nullptr);

UiaTracing::TextProvider::SetFocus(*this);
return Signal(UIA_AutomationFocusChangedEventId);
}

Expand All @@ -239,9 +220,6 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::SetFocus()

IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//ApiMsgGetSelection apiMsg;

_LockConsole();
auto Unlock = wil::scope_exit([&]() noexcept {
_UnlockConsole();
Expand All @@ -261,18 +239,12 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_
WRL::ComPtr<UiaTextRangeBase> range;
if (!_pData->IsSelectionActive())
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//apiMsg.AreaSelected = false;

// return a degenerate range at the cursor position
const Cursor& cursor = _getTextBuffer().GetCursor();
hr = CreateTextRange(this, cursor, _wordDelimiters, &range);
}
else
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//apiMsg.AreaSelected = true;

// get the selection range
hr = GetSelectionRange(this, _wordDelimiters, &range);
}
Expand All @@ -293,89 +265,71 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_
return hr;
}

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetSelection, &apiMsg);
UiaTracing::TextProvider::GetSelection(*this);
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetVisibleRanges, nullptr);

_LockConsole();
auto Unlock = wil::scope_exit([&]() noexcept {
_UnlockConsole();
});

RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal);
*ppRetVal = nullptr;

const auto bufferSize = _pData->GetTextBuffer().GetSize();
const auto viewport = bufferSize.ConvertToOrigin(_getViewport());

// make a safe array
const auto rowCount = viewport.Height();
*ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, gsl::narrow<ULONG>(rowCount));
*ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1);
if (*ppRetVal == nullptr)
{
return E_OUTOFMEMORY;
}

// stuff each visible line in the safearray
for (short i = 0; i < rowCount; ++i)
WRL::ComPtr<UiaTextRangeBase> range;
const auto bufferSize = _pData->GetTextBuffer().GetSize();
const auto viewport = bufferSize.ConvertToOrigin(_getViewport());

const COORD start{ viewport.Left(), viewport.Top() };
const COORD end{ viewport.Left(), viewport.BottomExclusive() };

auto hr = CreateTextRange(this, start, end, _wordDelimiters, &range);
if (FAILED(hr))
{
// end is exclusive so add 1
const COORD start{ viewport.Left(), viewport.Top() + i };
const COORD end{ start.X, start.Y + 1 };

HRESULT hr = S_OK;
WRL::ComPtr<UiaTextRangeBase> range;
hr = CreateTextRange(this,
start,
end,
_wordDelimiters,
&range);
if (FAILED(hr))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}

LONG currentIndex = gsl::narrow<LONG>(i);
hr = SafeArrayPutElement(*ppRetVal, &currentIndex, range.Detach());
if (FAILED(hr))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}
UiaTracing::TextProvider::GetVisibleRanges(*this, *range.Get());

LONG currentIndex = 0;
hr = SafeArrayPutElement(*ppRetVal, &currentIndex, range.Detach());
if (FAILED(hr))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}

return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromChild(_In_ IRawElementProviderSimple* /*childElement*/,
_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::RangeFromChild, nullptr);

RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal);
*ppRetVal = nullptr;

WRL::ComPtr<UiaTextRangeBase> utr;
RETURN_IF_FAILED(CreateTextRange(this, _wordDelimiters, &utr));
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
UiaTracing::TextProvider::RangeFromChild(*this, *utr.Get());
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point,
_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::RangeFromPoint, nullptr);

RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal);
*ppRetVal = nullptr;

Expand All @@ -385,32 +339,29 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point,
_wordDelimiters,
&utr));
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
UiaTracing::TextProvider::RangeFromPoint(*this, point, *utr.Get());
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetDocumentRange, nullptr);

RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal);
*ppRetVal = nullptr;

WRL::ComPtr<UiaTextRangeBase> utr;
RETURN_IF_FAILED(CreateTextRange(this, _wordDelimiters, &utr));
RETURN_IF_FAILED(utr->ExpandToEnclosingUnit(TextUnit::TextUnit_Document));
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
UiaTracing::TextProvider::get_DocumentRange(*this, *utr.Get());
return S_OK;
}

IFACEMETHODIMP ScreenInfoUiaProviderBase::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, pRetVal);

// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr);

*pRetVal = SupportedTextSelection::SupportedTextSelection_Single;
UiaTracing::TextProvider::get_SupportedTextSelection(*this, *pRetVal);
return S_OK;
}

Expand Down
Loading

0 comments on commit 360c655

Please sign in to comment.