Add more object ID tracing for Accessibility (#5215)

## Summary of the Pull Request

In preparation for getting more accessibility-related issues, I added an ID to the `ScreenInfoUiaProvider` (SIUP) and abstracted the one from `UiaTextRange`. Using this, I noticed that we are creating SIUPs when a new tab/pane is created. This is _good_. This means that we need to somehow notify a UIA Client that out structure has changed, and we need to use the new SIUP because the old one has been removed.

I'll be investigating that more after this PR lands.
This commit is contained in:
Carlos Zamora 2020-04-03 13:06:47 -07:00 committed by GitHub
parent 9513d543b7
commit 286af380c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 128 additions and 42 deletions

54
src/types/IUiaTraceable.h Normal file
View file

@ -0,0 +1,54 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- IUiaTraceable.hpp
Abstract:
- This module is used for assigning and retrieving IDs to UIA objects
Author(s):
- Carlos Zamora (cazamor) Apr 2020
--*/
#pragma once
namespace Microsoft::Console::Types
{
typedef unsigned long long IdType;
constexpr IdType InvalidId = 0;
class IUiaTraceable
{
public:
const IdType GetId() const noexcept
{
return _id;
}
// Routine Description:
// - assigns an ID to the IUiaTraceable object if it doesn't have one
// Arguments:
// - id - the id value that we are trying to assign
// Return Value:
// - true if the assignment was successful, false otherwise (it already has an id).
bool AssignId(IdType id) noexcept
{
if (_id == InvalidId)
{
_id = id;
return true;
}
else
{
return false;
}
}
private:
// used to debug objects passed back and forth
// between the provider and the client
IdType _id{};
};
}

View file

@ -38,30 +38,31 @@ try
RETURN_HR_IF_NULL(E_INVALIDARG, pData);
_pData = pData;
_wordDelimiters = wordDelimiters;
UiaTracing::TextProvider::Constructor(*this);
return S_OK;
}
CATCH_RETURN();
[[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID id)
[[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID eventId)
{
HRESULT hr = S_OK;
// check to see if we're already firing this particular event
if (_signalFiringMapping.find(id) != _signalFiringMapping.end() &&
_signalFiringMapping[id] == true)
if (_signalFiringMapping.find(eventId) != _signalFiringMapping.end() &&
_signalFiringMapping[eventId] == true)
{
return hr;
}
try
{
_signalFiringMapping[id] = true;
_signalFiringMapping[eventId] = true;
}
CATCH_RETURN();
IRawElementProviderSimple* pProvider = this;
hr = UiaRaiseAutomationEvent(pProvider, id);
_signalFiringMapping[id] = false;
hr = UiaRaiseAutomationEvent(pProvider, eventId);
_signalFiringMapping[eventId] = false;
return hr;
}

View file

@ -24,6 +24,7 @@ Author(s):
#include "../buffer/out/textBuffer.hpp"
#include "UiaTextRangeBase.hpp"
#include "IUiaData.h"
#include "IUiaTraceable.h"
#include <UIAutomationCore.h>
@ -35,7 +36,8 @@ namespace Microsoft::Console::Types
class Viewport;
class ScreenInfoUiaProviderBase :
public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, IRawElementProviderSimple, IRawElementProviderFragment, ITextProvider>
public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, IRawElementProviderSimple, IRawElementProviderFragment, ITextProvider>,
public IUiaTraceable
{
public:
virtual HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters = UiaTextRangeBase::DefaultWordDelimiter) noexcept;

View file

@ -9,8 +9,6 @@
using namespace Microsoft::Console::Types;
IdType UiaTextRangeBase::id = 1;
// degenerate range constructor.
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ std::wstring_view wordDelimiters) noexcept
@ -26,9 +24,6 @@ try
_blockRange = false;
_wordDelimiters = wordDelimiters;
_id = id;
++id;
UiaTracing::TextRange::Constructor(*this);
return S_OK;
}
@ -123,19 +118,11 @@ try
_pData = a._pData;
_wordDelimiters = a._wordDelimiters;
_id = id;
++id;
UiaTracing::TextRange::Constructor(*this);
return S_OK;
}
CATCH_RETURN();
const IdType UiaTextRangeBase::GetId() const noexcept
{
return _id;
}
const COORD UiaTextRangeBase::GetEndpoint(TextPatternRangeEndpoint endpoint) const noexcept
{
switch (endpoint)

View file

@ -22,6 +22,7 @@ Author(s):
#include "../buffer/out/textBuffer.hpp"
#include "IUiaData.h"
#include "unicode.hpp"
#include "IUiaTraceable.h"
#include <UIAutomationCore.h>
#include <deque>
@ -32,18 +33,10 @@ Author(s):
class UiaTextRangeTests;
#endif
typedef unsigned long long IdType;
constexpr IdType InvalidId = 0;
namespace Microsoft::Console::Types
{
class UiaTracing;
class UiaTextRangeBase : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, ITextRangeProvider>
class UiaTextRangeBase : public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, ITextRangeProvider>, public IUiaTraceable
{
private:
static IdType id;
protected:
// indicates which direction a movement operation
// is going
@ -84,7 +77,6 @@ namespace Microsoft::Console::Types
UiaTextRangeBase& operator=(UiaTextRangeBase&&) = delete;
~UiaTextRangeBase() = default;
const IdType GetId() const noexcept;
const COORD GetEndpoint(TextPatternRangeEndpoint endpoint) const noexcept;
bool SetEndpoint(TextPatternRangeEndpoint endpoint, const COORD val) noexcept;
const bool IsDegenerate() const noexcept;
@ -141,10 +133,6 @@ namespace Microsoft::Console::Types
void Initialize(_In_ const UiaPoint point);
// used to debug objects passed back and forth
// between the provider and the client
IdType _id{};
// measure units in the form [_start, _end).
// These are in the TextBuffer coordinate space.
// NOTE: _start is inclusive, but _end is exclusive

View file

@ -15,6 +15,44 @@ TRACELOGGING_DEFINE_PROVIDER(g_UiaProviderTraceProvider,
using namespace Microsoft::Console::Types;
// The first valid ID is 1 for each of our traced UIA object types
// ID assignment is handled between UiaTracing and IUiaTraceable to...
// - prevent multiple objects with the same ID
// - only assign IDs if UiaTracing is enabled
// - ensure objects are only assigned an ID once
IdType UiaTracing::_utrId = 1;
IdType UiaTracing::_siupId = 1;
// Routine Description:
// - assign an ID to the UiaTextRange, if it doesn't have one already
// Arguments:
// - utr - the UiaTextRange we are assigning an ID to
// Return Value:
// - N/A
void UiaTracing::_assignId(UiaTextRangeBase& utr) noexcept
{
auto temp = utr.AssignId(_utrId);
if (temp)
{
++_utrId;
}
}
// Routine Description:
// - assign an ID to the ScreenInfoUiaProvider, if it doesn't have one already
// Arguments:
// - siup - the ScreenInfoUiaProvider we are assigning an ID to
// Return Value:
// - N/A
void UiaTracing::_assignId(ScreenInfoUiaProviderBase& siup) noexcept
{
auto temp = siup.AssignId(_siupId);
if (temp)
{
++_siupId;
}
}
UiaTracing::UiaTracing() noexcept
{
TraceLoggingRegister(g_UiaProviderTraceProvider);
@ -25,9 +63,11 @@ UiaTracing::~UiaTracing() noexcept
TraceLoggingUnregister(g_UiaProviderTraceProvider);
}
inline std::wstring UiaTracing::_getValue(const ScreenInfoUiaProviderBase& /*siup*/) noexcept
inline std::wstring UiaTracing::_getValue(const ScreenInfoUiaProviderBase& siup) noexcept
{
return L" NO IDENTIFYING DATA";
std::wstringstream stream;
stream << "_id: " << siup.GetId();
return stream.str();
}
inline std::wstring UiaTracing::_getValue(const UiaTextRangeBase& utr) noexcept
@ -86,11 +126,12 @@ inline std::wstring UiaTracing::_getValue(const TextUnit unit) noexcept
}
}
void UiaTracing::TextRange::Constructor(const UiaTextRangeBase& result) noexcept
void UiaTracing::TextRange::Constructor(UiaTextRangeBase& result) noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
_assignId(result);
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"UiaTextRange::Constructor",
@ -99,11 +140,12 @@ void UiaTracing::TextRange::Constructor(const UiaTextRangeBase& result) noexcept
}
}
void UiaTracing::TextRange::Clone(const UiaTextRangeBase& utr, const UiaTextRangeBase& result) noexcept
void UiaTracing::TextRange::Clone(const UiaTextRangeBase& utr, UiaTextRangeBase& result) noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
_assignId(result);
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"UiaTextRange::Clone",
@ -360,11 +402,12 @@ void UiaTracing::TextRange::GetChildren(const UiaTextRangeBase& result) noexcept
}
}
void UiaTracing::TextProvider::Constructor(const ScreenInfoUiaProviderBase& result) noexcept
void UiaTracing::TextProvider::Constructor(ScreenInfoUiaProviderBase& result) noexcept
{
EnsureRegistration();
if (TraceLoggingProviderEnabled(g_UiaProviderTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
{
_assignId(result);
TraceLoggingWrite(
g_UiaProviderTraceProvider,
"ScreenInfoUiaProvider::Constructor",

View file

@ -32,8 +32,8 @@ namespace Microsoft::Console::Types
class TextRange final
{
public:
static void Constructor(const UiaTextRangeBase& result) noexcept;
static void Clone(const UiaTextRangeBase& base, const UiaTextRangeBase& result) noexcept;
static void Constructor(UiaTextRangeBase& result) noexcept;
static void Clone(const UiaTextRangeBase& base, UiaTextRangeBase& result) noexcept;
static void Compare(const UiaTextRangeBase& base, const UiaTextRangeBase& other, bool result) noexcept;
static void CompareEndpoints(const UiaTextRangeBase& base, const TextPatternRangeEndpoint endpoint, const UiaTextRangeBase& other, TextPatternRangeEndpoint otherEndpoint, int result) noexcept;
static void ExpandToEnclosingUnit(TextUnit unit, const UiaTextRangeBase& result) noexcept;
@ -56,7 +56,7 @@ namespace Microsoft::Console::Types
class TextProvider final
{
public:
static void Constructor(const ScreenInfoUiaProviderBase& result) noexcept;
static void Constructor(ScreenInfoUiaProviderBase& result) noexcept;
static void get_ProviderOptions(const ScreenInfoUiaProviderBase& base, ProviderOptions options) noexcept;
static void GetPatternProvider(const ScreenInfoUiaProviderBase& base, PATTERNID patternId) noexcept;
static void GetPropertyValue(const ScreenInfoUiaProviderBase& base, PROPERTYID propertyId) noexcept;
@ -100,5 +100,12 @@ namespace Microsoft::Console::Types
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;
// these are used to assign IDs to new UiaTextRanges and ScreenInfoUiaProviders respectively
static IdType _utrId;
static IdType _siupId;
static void _assignId(UiaTextRangeBase& utr) noexcept;
static void _assignId(ScreenInfoUiaProviderBase& siup) noexcept;
};
}

View file

@ -50,6 +50,7 @@
<ClInclude Include="..\inc\Utf16Parser.hpp" />
<ClInclude Include="..\IUiaData.h" />
<ClInclude Include="..\IUiaEventDispatcher.h" />
<ClInclude Include="..\IUiaTraceable.h" />
<ClInclude Include="..\IUiaWindow.h" />
<ClInclude Include="..\TermControlUiaTextRange.hpp" />
<ClInclude Include="..\TermControlUiaProvider.hpp" />

View file

@ -176,6 +176,9 @@
<ClInclude Include="..\UiaTracing.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\IUiaTraceable.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />