terminal/src/types/UiaTracing.h
Carlos Zamora a0e5085b49
Expose Text Attributes to UI Automation (#10336)
## Summary of the Pull Request
This implements `GetAttributeValue` and `FindAttribute` for `UiaTextRangeBase` (the shared `ITextRangeProvider` for Conhost and Windows Terminal). This also updates `UiaTracing` to collect more useful information on these function calls. 

## References
#7000 - Epic
[Text Attribute Identifiers](https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-textattribute-ids)
[ITextRangeProvider::GetAttributeValue](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nf-uiautomationcore-itextrangeprovider-getattributevalue)
[ITextRangeProvider::FindAttribute](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nf-uiautomationcore-itextrangeprovider-findattribute)

## PR Checklist
* [X] Closes #2161 
* [X] Tests added/passed

## Detailed Description of the Pull Request / Additional comments
- `TextBuffer`:
   - Exposes a new `TextBufferCellIterator` that takes in an end position. This simplifies the logic drastically as we can now use this iterator to navigate through the text buffer. The iterator can also expose the position in the buffer.
- `UiaTextRangeBase`:
   - Shared logic & helper functions:
      - Most of the text attributes are stored as `TextAttribute`s in the text buffer. To extract them, we generate an attribute verification function via `_getAttrVerificationFn()`, then use that to verify if a given cell has the desired attribute.
      - A few attributes are special (i.e. font name, font size, and "is read only"), in that they are (1) acquired differently and (2) consistent across the entire text buffer. These are handled separate from the attribute verification function.
   - `GetAttributeValue`: Retrieve the attribute verification of the first cell in the range. Then, verify that the entire range has that attribute by iterating through the text range. If a cell does not have that attribute, return the "reserved mixed attribute value".
   - `FindAttribute`: Iterate through the text range and leverage the attribute verification function to find the first contiguous range with that attribute. Then, make the end exclusive and output a `UiaTextRangeBase`. This function must be able to perform a search backwards, so we abstract the "start" and "end" into `resultFirstAnchor` and `resultSecondAnchor`, then perform post processing to output a valid `UiaTextRangeBase`.
- `UiaTracing`:
   - `GetAttributeValue`: Log uia text range, desired attribute, resulting attribute metadata, and the type of the result.
   - `FindAttribute`: Log uia text range, desired attribute and attribute metadata, if we were searching backwards, the type of the result, and the resulting text range.
   - `AttributeType` is a nice way to understand/record if the result was either of the reserved UIA values, a normal result, or an error.
- `UiaTextRangeTests`:
   - `GetAttributeValue`:
      - verify that we know which attributes we support
      - test each of the known text attributes (expecting 100% code coverage for `_getAttrVerificationFn()`)
   - `FindAttribute`: 
      - test each of the known _special_ text attributes
      - test `IsItalic`. NOTE: I'm explicitly only testing one of the standard text attributes because the logic is largely the same between all of them and they leverage `_getAttrVerificationFn()`.

## Validation Steps Performed
- @codeofdusk has been testing this Conhost build
- Tests added for Conhost and shared implementation
- Windows Terminal changes were manually verified using accessibility insights and NVDA
2021-07-09 23:21:35 +00:00

123 lines
6.5 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- UiaTracing.hpp
Abstract:
- This module is used for recording tracing/debugging information to the telemetry ETW channel
- The data is not automatically broadcast to telemetry backends as it does not set the TELEMETRY keyword.
- NOTE: Many functions in this file appear to be copy/pastes. This is because the TraceLog documentation warns
to not be "cute" in trying to reduce its macro usages with variables as it can cause unexpected behavior.
Author(s):
- Carlos Zamora (cazamor) Feb 2020
--*/
#pragma once
#include <winmeta.h>
#include <TraceLoggingProvider.h>
#include "UiaTextRangeBase.hpp"
#include "ScreenInfoUiaProviderBase.h"
TRACELOGGING_DECLARE_PROVIDER(g_UiaProviderTraceProvider);
namespace Microsoft::Console::Types
{
class UiaTracing final
{
public:
enum class AttributeType
{
Standard,
Mixed,
Unsupported,
Error
};
class TextRange final
{
public:
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;
static void FindAttribute(const UiaTextRangeBase& base, TEXTATTRIBUTEID attributeId, VARIANT val, BOOL searchBackwards, const UiaTextRangeBase& result, AttributeType attrType = AttributeType::Standard) noexcept;
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, AttributeType attrType = AttributeType::Standard) noexcept;
static void GetBoundingRectangles(const UiaTextRangeBase& base) 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;
static void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, const UiaTextRangeBase& other, TextPatternRangeEndpoint otherEndpoint, const UiaTextRangeBase& result) noexcept;
static void Select(const UiaTextRangeBase& base) noexcept;
static void AddToSelection(const UiaTextRangeBase& base) noexcept;
static void RemoveFromSelection(const UiaTextRangeBase& base) noexcept;
static void ScrollIntoView(bool alignToTop, const UiaTextRangeBase& result) noexcept;
static void GetChildren(const UiaTextRangeBase& base) noexcept;
};
class TextProvider final
{
public:
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;
static void get_HostRawElementProvider(const ScreenInfoUiaProviderBase& base) noexcept;
static void GetRuntimeId(const ScreenInfoUiaProviderBase& base) noexcept;
static void GetEmbeddedFragmentRoots(const ScreenInfoUiaProviderBase& base) noexcept;
static void SetFocus(const ScreenInfoUiaProviderBase& base) noexcept;
static void GetSelection(const ScreenInfoUiaProviderBase& base, const UiaTextRangeBase& result) noexcept;
static void GetVisibleRanges(const ScreenInfoUiaProviderBase& base, const UiaTextRangeBase& result) noexcept;
static void RangeFromChild(const ScreenInfoUiaProviderBase& base, const UiaTextRangeBase& result) noexcept;
static void RangeFromPoint(const ScreenInfoUiaProviderBase& base, UiaPoint point, const UiaTextRangeBase& result) noexcept;
static void get_DocumentRange(const ScreenInfoUiaProviderBase& base, const UiaTextRangeBase& result) noexcept;
static void get_SupportedTextSelection(const ScreenInfoUiaProviderBase& base, SupportedTextSelection result) noexcept;
};
class Signal final
{
public:
static void SelectionChanged() noexcept;
static void TextChanged() noexcept;
static void CursorChanged() noexcept;
};
private:
// Implement this as a singleton class.
static UiaTracing& EnsureRegistration() noexcept
{
static UiaTracing instance;
return instance;
}
// Used to prevent multiple instances
UiaTracing() noexcept;
~UiaTracing() noexcept;
UiaTracing(UiaTracing const&) = delete;
UiaTracing(UiaTracing&&) = delete;
UiaTracing& operator=(const UiaTracing&) = delete;
UiaTracing& operator=(UiaTracing&&) = delete;
static inline std::wstring _getValue(const ScreenInfoUiaProviderBase& siup) noexcept;
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;
static inline std::wstring _getValue(const AttributeType attrType) noexcept;
static inline std::wstring _getValue(const VARIANT val) 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;
};
}