terminal/src/types/TermControlUiaProvider.cpp
Carlos Zamora f012cb92fc [deadlock fix] Remove lock for SIUP::GetSelectionRange() (#11386)
## Summary of the Pull Request
The deadlock was caused by `ScreenInfoUiaProviderBase::GetSelection()` calling `TermControlUiaProvider::GetSelectionRange` (both of which attempted to lock the console). This PR removes the lock and initialization check from `TermControlUiaProvider`. It is no longer necessary because the only one that calls it is `SIUPB::GetSelection()`.

Additionally, this adds some code that was useful in debugging this race condition. That should help us figure out any locking issues that may come up in the future.

## References
#11312
Closes #11385 

## Validation Steps Performed
 Repro steps don't cause hang
2021-09-30 15:16:22 -07:00

178 lines
6.6 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "TermControlUiaProvider.hpp"
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, uiaData);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(uiaData));
_controlInfo = controlInfo;
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
*ppProvider = nullptr;
if (direction == NavigateDirection_Parent)
{
try
{
// TODO GitHub #2102: UIA Tree Navigation
//_pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider));
}
catch (...)
{
*ppProvider = nullptr;
return wil::ResultFromCaughtException();
}
RETURN_IF_NULL_ALLOC(*ppProvider);
}
// For the other directions the default of nullptr is correct
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect)
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr);
const RECT rc = _controlInfo->GetBounds();
pRect->left = rc.left;
pRect->top = rc.top;
pRect->width = static_cast<double>(rc.right) - static_cast<double>(rc.left);
pRect->height = static_cast<double>(rc.bottom) - static_cast<double>(rc.top);
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept
{
try
{
return _controlInfo->GetHostUiaProvider(ppProvider);
}
CATCH_RETURN();
}
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr);
try
{
// TODO GitHub #2102: UIA Tree Navigation - the special fragments that knows about all of its descendants is called a fragment root
//_pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider));
*ppProvider = nullptr;
}
catch (...)
{
*ppProvider = nullptr;
return wil::ResultFromCaughtException();
}
RETURN_IF_NULL_ALLOC(*ppProvider);
return S_OK;
}
const COORD TermControlUiaProvider::GetFontSize() const
{
return _controlInfo->GetFontSize();
}
const RECT TermControlUiaProvider::GetPadding() const
{
return _controlInfo->GetPadding();
}
const double TermControlUiaProvider::GetScaleFactor() const
{
return _controlInfo->GetScaleFactor();
}
void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
{
_controlInfo->ChangeViewport(NewWindow);
}
HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
const auto start = _pData->GetSelectionAnchor();
// we need to make end exclusive
auto end = _pData->GetSelectionEnd();
_pData->GetTextBuffer().GetSize().IncrementInBounds(end, true);
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, _pData->IsBlockSelection(), wordDelimiters));
*ppUtr = result;
return S_OK;
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, wordDelimiters));
*ppUtr = result;
return S_OK;
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters,
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
*ppUtr = result;
return S_OK;
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters,
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, false, wordDelimiters));
*ppUtr = result;
return S_OK;
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
const UiaPoint point,
const std::wstring_view wordDelimiters,
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
*ppUtr = result;
return S_OK;
}