Files
terminal/src/cascadia/TerminalControl/XamlUiaTextRange.cpp
glenrgordon b08dc61a9c Eliminate two memory leaks (#16597)
In WindowsTerminal, there was a leak of a BSTR with every call to
ITextRangeProvider::GetText, and a failure to call VariantClear in
ITextRange::GetAttributeValue when the value stored in the variant is
VT_BSTR. These were fixed by switching to wil::unique_bstr and
wil::unique_variant.

(cherry picked from commit da99d892f4)
Service-Card-Id: 91631736
Service-Version: 1.19
2024-01-25 16:46:02 -06:00

257 lines
10 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "XamlUiaTextRange.h"
#include "../types/TermControlUiaTextRange.hpp"
#include <UIAutomationClient.h>
#include <UIAutomationCoreApi.h>
#include "../types/UiaTracing.h"
// the same as COR_E_NOTSUPPORTED
// we don't want to import the CLR headers to get it
#define XAML_E_NOT_SUPPORTED 0x80131515L
namespace UIA
{
using ::ITextRangeProvider;
using ::SupportedTextSelection;
using ::TextPatternRangeEndpoint;
using ::TextUnit;
}
namespace XamlAutomation
{
using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection;
using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple;
using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider;
using winrt::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint;
using winrt::Windows::UI::Xaml::Automation::Text::TextUnit;
}
namespace winrt::Microsoft::Terminal::Control::implementation
{
XamlAutomation::ITextRangeProvider XamlUiaTextRange::Clone() const
{
UIA::ITextRangeProvider* pReturn;
THROW_IF_FAILED(_uiaProvider->Clone(&pReturn));
auto xutr = winrt::make_self<XamlUiaTextRange>(pReturn, _parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
bool XamlUiaTextRange::Compare(XamlAutomation::ITextRangeProvider pRange) const
{
auto self = winrt::get_self<XamlUiaTextRange>(pRange);
BOOL returnVal;
THROW_IF_FAILED(_uiaProvider->Compare(self->_uiaProvider.get(), &returnVal));
return returnVal;
}
int32_t XamlUiaTextRange::CompareEndpoints(XamlAutomation::TextPatternRangeEndpoint endpoint,
XamlAutomation::ITextRangeProvider pTargetRange,
XamlAutomation::TextPatternRangeEndpoint targetEndpoint)
{
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange);
int32_t returnVal;
THROW_IF_FAILED(_uiaProvider->CompareEndpoints(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
self->_uiaProvider.get(),
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint),
&returnVal));
return returnVal;
}
void XamlUiaTextRange::ExpandToEnclosingUnit(XamlAutomation::TextUnit unit) const
{
THROW_IF_FAILED(_uiaProvider->ExpandToEnclosingUnit(static_cast<UIA::TextUnit>(unit)));
}
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindAttribute(int32_t /*textAttributeId*/,
winrt::Windows::Foundation::IInspectable /*val*/,
bool /*searchBackward*/)
{
// TODO GitHub #2161: potential accessibility improvement
// we don't support this currently
throw winrt::hresult_not_implemented();
}
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindText(winrt::hstring text,
bool searchBackward,
bool ignoreCase)
{
UIA::ITextRangeProvider* pReturn;
const auto queryText = wil::make_bstr(text.c_str());
THROW_IF_FAILED(_uiaProvider->FindText(queryText.get(), searchBackward, ignoreCase, &pReturn));
auto xutr = winrt::make_self<XamlUiaTextRange>(pReturn, _parentProvider);
return *xutr;
}
winrt::Windows::Foundation::IInspectable XamlUiaTextRange::GetAttributeValue(int32_t textAttributeId) const
{
// Call the function off of the underlying UiaTextRange.
wil::unique_variant result;
THROW_IF_FAILED(_uiaProvider->GetAttributeValue(textAttributeId, result.addressof()));
// Convert the resulting VARIANT into a format that is consumable by XAML.
switch (result.vt)
{
case VT_BSTR:
{
return box_value(result.bstrVal);
}
case VT_I4:
{
// Surprisingly, `long` is _not_ a WinRT type.
// So we have to use `int32_t` to make sure this is output properly.
// Otherwise, you'll get "Attribute does not exist" out the other end.
return box_value<int32_t>(result.lVal);
}
case VT_R8:
{
return box_value(result.dblVal);
}
case VT_BOOL:
{
return box_value<bool>(result.boolVal);
}
case VT_UNKNOWN:
{
// This one is particularly special.
// We might return a special value like UiaGetReservedMixedAttributeValue
// or UiaGetReservedNotSupportedValue.
// Some text attributes may return a real value, however, none of those
// are supported at this time.
// So we need to figure out what was actually intended to be returned.
com_ptr<IUnknown> mixedAttributeVal;
UiaGetReservedMixedAttributeValue(mixedAttributeVal.put());
if (result.punkVal == mixedAttributeVal.get())
{
return Windows::UI::Xaml::DependencyProperty::UnsetValue();
}
[[fallthrough]];
}
default:
{
// We _need_ to return XAML_E_NOT_SUPPORTED here.
// Returning nullptr is an improper implementation of it being unsupported.
// UIA Clients rely on this HRESULT to signify that the requested attribute is undefined.
// Anything else will result in the UIA Client refusing to read when navigating by word
// Magically, this doesn't affect other forms of navigation...
winrt::throw_hresult(XAML_E_NOT_SUPPORTED);
}
}
}
void XamlUiaTextRange::GetBoundingRectangles(com_array<double>& returnValue) const
{
returnValue = {};
try
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetBoundingRectangles(&pReturnVal));
double* pVals;
THROW_IF_FAILED(SafeArrayAccessData(pReturnVal, (void**)&pVals));
long lBound, uBound;
THROW_IF_FAILED(SafeArrayGetLBound(pReturnVal, 1, &lBound));
THROW_IF_FAILED(SafeArrayGetUBound(pReturnVal, 1, &uBound));
auto count = uBound - lBound + 1;
std::vector<double> vec;
vec.reserve(count);
for (auto i = 0; i < count; i++)
{
auto element = pVals[i];
vec.push_back(element);
}
winrt::com_array<double> result{ vec };
returnValue = std::move(result);
}
catch (...)
{
}
}
XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement()
{
::Microsoft::Console::Types::UiaTracing::TextRange::GetEnclosingElement(*static_cast<::Microsoft::Console::Types::UiaTextRangeBase*>(_uiaProvider.get()));
return _parentProvider;
}
winrt::hstring XamlUiaTextRange::GetText(int32_t maxLength) const
{
wil::unique_bstr returnVal;
THROW_IF_FAILED(_uiaProvider->GetText(maxLength, returnVal.put()));
return winrt::hstring{ returnVal.get(), SysStringLen(returnVal.get()) };
}
int32_t XamlUiaTextRange::Move(XamlAutomation::TextUnit unit,
int32_t count)
{
int returnVal;
THROW_IF_FAILED(_uiaProvider->Move(static_cast<UIA::TextUnit>(unit),
count,
&returnVal));
return returnVal;
}
int32_t XamlUiaTextRange::MoveEndpointByUnit(XamlAutomation::TextPatternRangeEndpoint endpoint,
XamlAutomation::TextUnit unit,
int32_t count) const
{
int returnVal;
THROW_IF_FAILED(_uiaProvider->MoveEndpointByUnit(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
static_cast<UIA::TextUnit>(unit),
count,
&returnVal));
return returnVal;
}
void XamlUiaTextRange::MoveEndpointByRange(XamlAutomation::TextPatternRangeEndpoint endpoint,
XamlAutomation::ITextRangeProvider pTargetRange,
XamlAutomation::TextPatternRangeEndpoint targetEndpoint) const
{
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange);
THROW_IF_FAILED(_uiaProvider->MoveEndpointByRange(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
/*pTargetRange*/ self->_uiaProvider.get(),
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint)));
}
void XamlUiaTextRange::Select() const
{
THROW_IF_FAILED(_uiaProvider->Select());
}
void XamlUiaTextRange::AddToSelection() const
{
// we don't support this
throw winrt::hresult_not_implemented();
}
void XamlUiaTextRange::RemoveFromSelection() const
{
// we don't support this
throw winrt::hresult_not_implemented();
}
void XamlUiaTextRange::ScrollIntoView(bool alignToTop) const
{
THROW_IF_FAILED(_uiaProvider->ScrollIntoView(alignToTop));
}
winrt::com_array<XamlAutomation::IRawElementProviderSimple> XamlUiaTextRange::GetChildren() const
{
// we don't have any children
return {};
}
}