Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIA: Fix GetVisibleRanges() and add Tracing #4495

Merged
11 commits merged into from
Feb 20, 2020
Merged
58 changes: 24 additions & 34 deletions src/types/ScreenInfoUiaProviderBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,50 +304,40 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben
});

RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal);
*ppRetVal = nullptr;
WRL::ComPtr<UiaTextRangeBase> range;

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)
{
// 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;
hr = CreateTextRange(this,
start,
end,
_wordDelimiters,
&range);
if (FAILED(hr))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}
WRL::ComPtr<UiaTextRangeBase> range;
const auto bufferSize = _pData->GetTextBuffer().GetSize();
const auto viewport = bufferSize.ConvertToOrigin(_getViewport());

LONG currentIndex = gsl::narrow<LONG>(i);
hr = SafeArrayPutElement(*ppRetVal, &currentIndex, range.Detach());
if (FAILED(hr))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}
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))
{
SafeArrayDestroy(*ppRetVal);
*ppRetVal = nullptr;
return hr;
}

UiaTracing::TextProvider::GetVisibleRanges(*this, *range.Get());
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

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

return S_OK;
}

Expand Down
165 changes: 77 additions & 88 deletions src/types/UiaTextRangeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,94 +462,117 @@ try
RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr);
*ppRetVal = nullptr;

#pragma warning(suppress : 26447) // QueryInterface's exception should be caught by the try-CATCH_RETURN block to allow this to be noexcept
const auto hr = _pProvider->QueryInterface(IID_PPV_ARGS(ppRetVal));
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
// TODO CARLOS: fix tracing call
//UiaTracing::TextRange::GetEnclosingElement(*this, static_cast<ScreenInfoUiaProviderBase*>(*ppRetVal));
UiaTracing::TextRange::GetEnclosingElement(*this);
return hr;
}
CATCH_RETURN();

IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) noexcept
try
{
_pData->LockConsole();
auto Unlock = wil::scope_exit([&]() noexcept {
_pData->UnlockConsole();
});

RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr);
*pRetVal = nullptr;

std::wstring wstr = L"";

if (maxLength < -1)
{
return E_INVALIDARG;
}

const auto text = _getTextValue(maxLength);

*pRetVal = SysAllocString(text.c_str());
UiaTracing::TextRange::GetText(*this, maxLength, text);
return S_OK;
}
CATCH_RETURN();

// Method Description:
// - Helper method for GetText(). Retrieves the text that the UiaTextRange encompasses as a wstring
// Arguments:
// - maxLength - the maximum size of the retrieved text. -1 means we don't care about the size.
// Return Value:
// - the text that the UiaTextRange encompasses
#pragma warning(push)
#pragma warning(disable : 26447) // compiler isn't filtering throws inside the try/catch
std::wstring UiaTextRangeBase::_getTextValue(int maxLength) const noexcept
try
{
_pData->LockConsole();
auto Unlock = wil::scope_exit([&]() noexcept {
_pData->UnlockConsole();
});

if (IsDegenerate())
{
return {};
}

std::wstring result{};

// the caller must pass in a value for the max length of the text
// to retrieve. a value of -1 means they don't want the text
// truncated.
const bool getPartialText = maxLength != -1;

if (!IsDegenerate())
{
// if _end is at 0, we ignore that row because _end is exclusive
const auto& buffer = _pData->GetTextBuffer();
const short totalRowsInRange = (_end.X == buffer.GetSize().Left()) ?
base::ClampSub(_end.Y, _start.Y) :
base::ClampAdd(base::ClampSub(_end.Y, _start.Y), base::ClampedNumeric<short>(1));
const short lastRowInRange = _start.Y + totalRowsInRange - 1;
// if _end is at 0, we ignore that row because _end is exclusive
const auto& buffer = _pData->GetTextBuffer();
const short totalRowsInRange = (_end.X == buffer.GetSize().Left()) ?
base::ClampSub(_end.Y, _start.Y) :
base::ClampAdd(base::ClampSub(_end.Y, _start.Y), base::ClampedNumeric<short>(1));
const short lastRowInRange = _start.Y + totalRowsInRange - 1;

short currentScreenInfoRow = 0;
for (short i = 0; i < totalRowsInRange; ++i)
short currentScreenInfoRow = 0;
for (short i = 0; i < totalRowsInRange; ++i)
{
currentScreenInfoRow = _start.Y + i;
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
if (row.GetCharRow().ContainsText())
{
currentScreenInfoRow = _start.Y + i;
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
if (row.GetCharRow().ContainsText())
const size_t rowRight = row.GetCharRow().MeasureRight();
size_t startIndex = 0;
size_t endIndex = rowRight;
if (currentScreenInfoRow == _start.Y)
{
const size_t rowRight = row.GetCharRow().MeasureRight();
size_t startIndex = 0;
size_t endIndex = rowRight;
if (currentScreenInfoRow == _start.Y)
{
startIndex = _start.X;
}

if (currentScreenInfoRow == _end.Y)
{
// prevent the end from going past the last non-whitespace char in the row
endIndex = std::max<size_t>(startIndex + 1, std::min(gsl::narrow_cast<size_t>(_end.X), rowRight));
}

// if startIndex >= endIndex then _start is
// further to the right than the last
// non-whitespace char in the row so there
// wouldn't be any text to grab.
if (startIndex < endIndex)
{
wstr += row.GetText().substr(startIndex, endIndex - startIndex);
}
startIndex = _start.X;
}

if (currentScreenInfoRow != lastRowInRange)
if (currentScreenInfoRow == _end.Y)
{
wstr += L"\r\n";
// prevent the end from going past the last non-whitespace char in the row
endIndex = std::max<size_t>(startIndex + 1, std::min(gsl::narrow_cast<size_t>(_end.X), rowRight));
}

if (getPartialText && wstr.size() > static_cast<size_t>(maxLength))
// if startIndex >= endIndex then _start is
// further to the right than the last
// non-whitespace char in the row so there
// wouldn't be any text to grab.
if (startIndex < endIndex)
{
wstr.resize(maxLength);
break;
result += row.GetText().substr(startIndex, endIndex - startIndex);
}
}

if (currentScreenInfoRow != lastRowInRange)
{
result += L"\r\n";
}

if (getPartialText && result.size() > static_cast<size_t>(maxLength))
{
result.resize(maxLength);
break;
}
}

*pRetVal = SysAllocString(wstr.c_str());
UiaTracing::TextRange::GetText(*this, maxLength, wstr);
return S_OK;
return result;
}
CATCH_RETURN();
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return {};
}
#pragma warning(pop)

IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
_In_ int count,
Expand Down Expand Up @@ -1178,37 +1201,3 @@ RECT UiaTextRangeBase::_getTerminalRect() const
gsl::narrow<LONG>(result.top + result.height)
};
}

#pragma warning(push)
#pragma warning(disable : 26447) // compiler isn't filtering throws inside the try/catch
std::wstring UiaTextRangeBase::_getTextValue() const noexcept
try
{
_pData->LockConsole();
auto Unlock = wil::scope_exit([&]() noexcept {
_pData->UnlockConsole();
});

if (IsDegenerate())
{
return {};
}

std::wstringstream stream;
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = buffer.GetSize();

auto pos = _start;
while (std::abs(bufferSize.CompareInBounds(pos, _end, true)) > 0)
{
stream << buffer.GetTextDataAt(pos)->data();
bufferSize.IncrementInBounds(pos, true);
}

return stream.str();
}
catch (...)
{
return {};
}
#pragma warning(pop)
2 changes: 1 addition & 1 deletion src/types/UiaTextRangeBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ namespace Microsoft::Console::Types
// This is used by tracing to extract the text value
// that the UiaTextRange currently encompasses.
// GetText() cannot be used as it's not const
std::wstring _getTextValue() const noexcept;
std::wstring _getTextValue(int maxLength = -1) const noexcept;
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

RECT _getTerminalRect() const;

Expand Down
28 changes: 7 additions & 21 deletions src/types/UiaTracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@
// we need to disable a few warnings because
// the TraceLogging code is out of our control
#pragma warning(push)
#pragma warning(disable : 26447)
#pragma warning(disable : 26477)
#pragma warning(disable : 26482)
#pragma warning(disable : 26496)
#pragma warning(disable : 26485)
#pragma warning(disable : 26494)
#pragma warning(disable : 26446)
#pragma warning(disable : 26446 26447 26477 26482 26485 26494 26496)
TRACELOGGING_DEFINE_PROVIDER(g_UiaProviderTraceProvider,
"Microsoft.Windows.Console.UIA",
// tl:{e7ebce59-2161-572d-b263-2f16a6afb9e5}
Expand All @@ -31,19 +25,12 @@ UiaTracing::~UiaTracing() noexcept
TraceLoggingUnregister(g_UiaProviderTraceProvider);
}

std::wstring UiaTracing::_getValue(const ScreenInfoUiaProviderBase& /*siup*/) noexcept
try
inline std::wstring UiaTracing::_getValue(const ScreenInfoUiaProviderBase& /*siup*/) noexcept
{
std::wstringstream stream;
stream << " NO IDENTIFYING DATA";
return stream.str();
}
catch (...)
{
return {};
return L" NO IDENTIFYING DATA";
}

std::wstring UiaTracing::_getValue(const UiaTextRangeBase& utr) noexcept
inline std::wstring UiaTracing::_getValue(const UiaTextRangeBase& utr) noexcept
try
{
const auto start = utr.GetEndpoint(TextPatternRangeEndpoint_Start);
Expand All @@ -63,7 +50,7 @@ catch (...)
return {};
}

std::wstring UiaTracing::_getValue(const TextPatternRangeEndpoint endpoint) noexcept
inline std::wstring UiaTracing::_getValue(const TextPatternRangeEndpoint endpoint) noexcept
{
switch (endpoint)
{
Expand All @@ -76,7 +63,7 @@ std::wstring UiaTracing::_getValue(const TextPatternRangeEndpoint endpoint) noex
}
}

std::wstring UiaTracing::_getValue(const TextUnit unit) noexcept
inline std::wstring UiaTracing::_getValue(const TextUnit unit) noexcept
{
switch (unit)
{
Expand Down Expand Up @@ -203,14 +190,13 @@ void UiaTracing::TextRange::GetBoundingRectangles(const UiaTextRangeBase& utr) n
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}

void UiaTracing::TextRange::GetEnclosingElement(const UiaTextRangeBase& utr, const ScreenInfoUiaProviderBase& siup) noexcept
void UiaTracing::TextRange::GetEnclosingElement(const UiaTextRangeBase& utr) noexcept
{
EnsureRegistration();
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"UiaTextRange::GetEnclosingElement",
TraceLoggingValue(_getValue(utr).c_str(), "base"),
TraceLoggingValue(_getValue(siup).c_str(), "parent"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}

Expand Down
10 changes: 5 additions & 5 deletions src/types/UiaTracing.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace Microsoft::Console::Types
static void FindText(const UiaTextRangeBase& base, std::wstring text, bool searchBackward, bool ignoreCase, const UiaTextRangeBase& result) noexcept;
static void GetAttributeValue(const UiaTextRangeBase& base, TEXTATTRIBUTEID id, VARIANT result) noexcept;
static void GetBoundingRectangles(const UiaTextRangeBase& base) noexcept;
static void GetEnclosingElement(const UiaTextRangeBase& base, const ScreenInfoUiaProviderBase& siup) noexcept;
static void GetEnclosingElement(const UiaTextRangeBase& base) noexcept;
static void GetText(const UiaTextRangeBase& base, int maxLength, std::wstring result) noexcept;
static void Move(TextUnit unit, int count, int resultCount, const UiaTextRangeBase& result) noexcept;
static void MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count, int resultCount, const UiaTextRangeBase& result) noexcept;
Expand Down Expand Up @@ -88,9 +88,9 @@ namespace Microsoft::Console::Types
UiaTracing& operator=(const UiaTracing&) = delete;
UiaTracing& operator=(UiaTracing&&) = delete;

static std::wstring _getValue(const ScreenInfoUiaProviderBase& siup) noexcept;
static std::wstring _getValue(const UiaTextRangeBase& utr) noexcept;
static std::wstring _getValue(const TextPatternRangeEndpoint endpoint) noexcept;
static std::wstring _getValue(const TextUnit unit) noexcept;
static inline std::wstring _getValue(const ScreenInfoUiaProviderBase& siup) noexcept;
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
static inline std::wstring _getValue(const UiaTextRangeBase& utr) noexcept;
static inline std::wstring _getValue(const TextPatternRangeEndpoint endpoint) noexcept;
static inline std::wstring _getValue(const TextUnit unit) noexcept;
};
}