# 機能設計書 109-UIオートメーション対応

## 概要

本ドキュメントは、Windows TerminalのUIオートメーション（UIA）対応機能について記述する。スクリーンリーダー等の支援技術（Assistive Technology）と連携するため、TermControlにAutomationPeerを提供し、ITextProviderパターンを実装してテキストバッファへのアクセシビリティアクセスを実現する。

### 本機能の処理概要

**業務上の目的・背景**：Windows Terminalはアクセシビリティへの対応が求められる。スクリーンリーダー（Narrator、NVDA、JAWS等）がターミナルのテキスト内容、選択状態、カーソル位置を取得し、視覚障害のあるユーザーがターミナルを操作できるようにする必要がある。

**機能の利用シーン**：スクリーンリーダーがTermControlにフォーカスした際、AutomationPeerを通じてテキスト内容を読み上げる。テキストの変更、選択の変更、カーソルの移動時にUIA通知イベントが発生し、スクリーンリーダーに状態変化を伝える。新しい出力テキストはRaiseNotificationEventで通知される。

**主要な処理内容**：
1. TermControlAutomationPeer：XAML UIツリーにアタッチされるFrameworkElementAutomationPeer
2. InteractivityAutomationPeer：ControlInteractivityに接続しITextProviderの実質的な実装を提供
3. IUiaEventDispatcherの実装：テキスト変更・選択変更・カーソル変更イベントの発行
4. 新規出力テキストのフィルタリング（キー入力エコーの除去）と通知
5. IControlAccessibilityInfoの実装：フォント・座標・ビューポート情報の提供

**関連システム・外部連携**：Windows UI Automation Framework、XAML Automation Peers、TermControlUiaProvider、スクリーンリーダー。

**権限による制御**：特段の権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | TermControl（ターミナル表示） | 補助機能 | スクリーンリーダー等との連携 |

## 機能種別

アクセシビリティ / UIオートメーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| owner (TermControl) | com_ptr<TermControl> | Yes | 対象のTermControl | 有効なポインタ |
| padding | Core::Padding | Yes | コントロールのパディング | - |
| interactivity | ControlInteractivity* | Yes | インタラクティビティ層 | 有効なポインタ |

### 入力データソース

- TermControlのテキストバッファ（RenderData経由）
- XAMLフレームワークからのAutomationPeer要求
- スクリーンリーダーからのUIA要求

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| TextRangeProvider | ITextRangeProvider | テキスト範囲の提供 |
| AutomationEvents | イベント | テキスト変更・選択変更・カーソル変更通知 |
| NotificationEvent | イベント | 新規出力テキスト通知 |

### 出力先

- スクリーンリーダーへのUIA通知
- Windows UI Automation Framework

## 処理フロー

### 処理シーケンス

```
1. AutomationPeer初期化
   |-- TermControlAutomationPeer生成
   |   |-- FrameworkElementAutomationPeerにTermControlをバインド
   |   |-- InteractivityAutomationPeerのイベントを購読
   |   |   +-- SelectionChanged, TextChanged, CursorChanged, NewOutput
   |   +-- ParentProviderとして自身を設定
   |
   +-- InteractivityAutomationPeer生成
       +-- TermControlUiaProviderを初期化 (MakeAndInitialize)

2. イベント通知フロー
   InteractivityAutomationPeer側
   |-- SignalSelectionChanged → SelectionChanged.raise
   |-- SignalTextChanged → TextChanged.raise
   |-- SignalCursorChanged → CursorChanged.raise
   +-- NotifyNewOutput → NewOutput.raise

   TermControlAutomationPeer側（UI Dispatcherで実行）
   |-- SignalSelectionChanged → RaiseAutomationEvent(TextPatternOnTextSelectionChanged)
   |-- SignalTextChanged → RaiseAutomationEvent(TextPatternOnTextChanged)
   |-- SignalCursorChanged → RaiseAutomationEvent(TextPatternOnTextSelectionChanged)
   +-- NotifyNewOutput → Sanitize → キー入力フィルタ → RaiseNotificationEvent

3. ITextProviderメソッド呼び出し
   TermControlAutomationPeer → InteractivityAutomationPeer に委譲
   |-- GetSelection → _uiaProvider->GetSelection → WrapArrayOfTextRangeProviders
   |-- GetVisibleRanges → _uiaProvider->GetVisibleRanges → WrapArrayOfTextRangeProviders
   |-- RangeFromPoint → _uiaProvider->RangeFromPoint → _CreateXamlUiaTextRange
   |-- RangeFromChild → _uiaProvider->RangeFromChild → _CreateXamlUiaTextRange
   |-- DocumentRange → _uiaProvider->get_DocumentRange → _CreateXamlUiaTextRange
   +-- SupportedTextSelection → _uiaProvider->get_SupportedTextSelection
```

### フローチャート

```mermaid
flowchart TD
    A[スクリーンリーダー] -->|UIA要求| B[TermControlAutomationPeer]
    B -->|委譲| C[InteractivityAutomationPeer]
    C -->|バッファアクセス| D[TermControlUiaProvider]
    D -->|テキスト取得| E[TextBuffer/RenderData]

    F[TextBuffer変更] -->|イベント| C
    C -->|raise| B
    B -->|Dispatcher.RunAsync| G[RaiseAutomationEvent]
    G -->|UIA通知| A

    H[新規出力] -->|NotifyNewOutput| C
    C -->|raise| B
    B -->|Sanitize+Filter| I[RaiseNotificationEvent]
    I -->|UIA通知| A
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-109-1 | クラス名固定 | GetClassNameCoreは"TermControl"を返却。JA WS等のスクリーンリーダーが依存 | 常時 |
| BR-109-2 | コントロールタイプ | AutomationControlTypeはText | 常時 |
| BR-109-3 | ライブ設定 | AutomationLiveSettingはPolite | 常時 |
| BR-109-4 | キー入力フィルタ | NotifyNewOutputで、キー入力のエコーを出力テキストから除去する | 出力通知時 |
| BR-109-5 | 制御文字除去 | 出力テキストからASCII制御文字（スペース未満、0x7F DEL）を除去。改行は保持 | 出力通知時 |
| BR-109-6 | カーソル変更通知 | カーソル変更時はTextPatternOnTextSelectionChangedを発行（TextEditTextChangedではない） | カーソル移動時 |

### 計算ロジック

特殊な計算ロジックはない。

## データベース操作仕様

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E_INVALIDARG | 引数不正 | GetHostUiaProviderでproviderがnull | RETURN_HR_IF |
| HRESULT失敗 | UIA操作失敗 | ITextRangeProvider操作失敗 | THROW_IF_FAILED |
| - | Dispatcher不在 | UIツリーから切断済み | 処理スキップ（returnで終了） |

### リトライ仕様

リトライは不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- UIA通知はDispatcher.RunAsyncで非同期実行されるため、メインスレッドをブロックしない
- NotifyNewOutputのキー入力フィルタリングは高頻度で呼ばれるため、低レイテンシが求められる
- RecordKeyEventでキー入力を記録し、出力のエコー除去に使用する

## セキュリティ考慮事項

- 出力テキストはSanitize関数で制御文字を除去してからスクリーンリーダーに渡す
- パスワード入力等の機密情報がスクリーンリーダーに伝わる可能性がある（ターミナルの特性上）

## 備考

- TermControlが UIツリーから削除された後のイベント発行を防ぐため、Close()で_termControlをクリアする（GH#13978）
- InteractivityAutomationPeerはタブティアアウト対応のために分離された（2021年5月）
- _CreateXamlUiaTextRangeで_parentProvider.ProviderFromPeer()を使用する（thisではなくparent）ことが重要（Narratorナビゲーションのため）
- RaiseNotificationEventのProcessingはAllに設定（キーボードイベントで中断可能）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造とインターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TermControlAutomationPeer.h | `src/cascadia/TerminalControl/TermControlAutomationPeer.h` | クラス宣言(41-86行目)。FrameworkElementAutomationPeer継承、IUiaEventDispatcher実装 |
| 1-2 | InteractivityAutomationPeer.h | `src/cascadia/TerminalControl/InteractivityAutomationPeer.h` | クラス宣言(36-89行目)。IUiaEventDispatcher + IControlAccessibilityInfo実装 |

**読解のコツ**: TermControlAutomationPeerはXAML UIツリーにアタッチされるpublic面、InteractivityAutomationPeerはバッファアクセスを担うinternal面。この2層構造はタブティアアウト（TermControlを別ウィンドウに移動）対応のために分離されている。

#### Step 2: 初期化と委譲パターンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TermControlAutomationPeer.cpp | `src/cascadia/TerminalControl/TermControlAutomationPeer.cpp` | コンストラクタ(69-86行目)でイベント購読、ITextProviderメソッド(338-367行目)の委譲 |
| 2-2 | InteractivityAutomationPeer.cpp | `src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp` | コンストラクタ(31-35行目)でTermControlUiaProvider初期化、ITextProvider(105-148行目) |

#### Step 3: イベント通知フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TermControlAutomationPeer.cpp | `src/cascadia/TerminalControl/TermControlAutomationPeer.cpp` | SignalSelectionChanged(132-150行目)、SignalTextChanged(158-176行目)、SignalCursorChanged(184-207行目)、NotifyNewOutput(209-270行目) |

**主要処理フロー**:
1. **38-46行目**: Sanitize関数で制御文字を除去（改行は保持）
2. **55-65行目**: IsReadable関数で読み取り可能かを判定
3. **108-117行目**: RecordKeyEventでキー入力をデキューに記録
4. **209-270行目**: NotifyNewOutputでSanitize後、キー入力エコーを除去し、可読テキストのみRaiseNotificationEventで通知

#### Step 4: IControlAccessibilityInfoを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | InteractivityAutomationPeer.cpp | `src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp` | GetFontSize(153-156行目)、GetBounds(158-161行目)、GetPadding(171-174行目)、ChangeViewport(176-179行目) |

### プログラム呼び出し階層図

```
TermControl (XAML UIコントロール)
    |
    +-- TermControlAutomationPeer (FrameworkElementAutomationPeer)
    |       +-- GetClassNameCore        → "TermControl"
    |       +-- GetAutomationControlTypeCore → Text
    |       +-- GetPatternCore(Text)    → this (ITextProvider)
    |       +-- GetLiveSettingCore      → Polite
    |       +-- GetNameCore             → プロファイル名 or タイトル
    |       |
    |       +-- GetSelection()          → _contentAutomationPeer.GetSelection()
    |       +-- GetVisibleRanges()      → _contentAutomationPeer.GetVisibleRanges()
    |       +-- RangeFromPoint()        → _contentAutomationPeer.RangeFromPoint()
    |       +-- DocumentRange()         → _contentAutomationPeer.DocumentRange()
    |       |
    |       +-- SignalSelectionChanged  → Dispatcher.RunAsync → RaiseAutomationEvent
    |       +-- SignalTextChanged       → Dispatcher.RunAsync → RaiseAutomationEvent
    |       +-- NotifyNewOutput         → Sanitize → Filter → RaiseNotificationEvent
    |
    +-- InteractivityAutomationPeer (ITextProvider実装)
            +-- TermControlUiaProvider (_uiaProvider)
            |       +-- GetSelection / GetVisibleRanges
            |       +-- RangeFromPoint / RangeFromChild
            |       +-- get_DocumentRange / get_SupportedTextSelection
            |
            +-- IControlAccessibilityInfo
            |       +-- GetFontSize / GetBounds / GetPadding
            |       +-- ChangeViewport / GetHostUiaProvider
            |
            +-- IUiaEventDispatcher
                    +-- SignalSelectionChanged → SelectionChanged.raise
                    +-- SignalTextChanged → TextChanged.raise
                    +-- SignalCursorChanged → CursorChanged.raise
                    +-- NotifyNewOutput → NewOutput.raise
```

### データフロー図

```
[入力]                              [処理]                          [出力]

スクリーンリーダー                   AutomationPeer                 応答
  UIA GetSelection ---------> TermControlAP → InteractivityAP --> ITextRangeProvider
  UIA GetVisibleRanges -----> TermControlAP → InteractivityAP --> ITextRangeProvider[]
  UIA RangeFromPoint -------> TermControlAP → InteractivityAP --> ITextRangeProvider

TextBuffer変更                      イベント通知
  テキスト変更 ---------> InteractivityAP::SignalTextChanged
                              → TermControlAP::SignalTextChanged
                              → RaiseAutomationEvent ---------> スクリーンリーダー

  新規出力 -----------> InteractivityAP::NotifyNewOutput
                              → TermControlAP::NotifyNewOutput
                              → Sanitize → キー入力除去
                              → RaiseNotificationEvent -------> スクリーンリーダー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TermControlAutomationPeer.h | `src/cascadia/TerminalControl/TermControlAutomationPeer.h` | ソース(ヘッダ) | XAML側AutomationPeer宣言 |
| TermControlAutomationPeer.cpp | `src/cascadia/TerminalControl/TermControlAutomationPeer.cpp` | ソース(実装) | XAML側AutomationPeer実装 |
| InteractivityAutomationPeer.h | `src/cascadia/TerminalControl/InteractivityAutomationPeer.h` | ソース(ヘッダ) | バッファ側AutomationPeer宣言 |
| InteractivityAutomationPeer.cpp | `src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp` | ソース(実装) | ITextProvider/IControlAccessibilityInfo実装 |
| TermControlUiaProvider.hpp | `src/types/TermControlUiaProvider.hpp` | ソース(ヘッダ) | Win32 UIA TextProviderラッパー |
| XamlUiaTextRange.h | `src/cascadia/TerminalControl/XamlUiaTextRange.h` | ソース(ヘッダ) | Win32→XAML TextRangeProvider変換 |
| IUiaEventDispatcher.h | `src/types/IUiaEventDispatcher.h` | ソース(ヘッダ) | UIA イベントインターフェース |
| IControlAccessibilityInfo.h | `src/types/IControlAccessibilityInfo.h` | ソース(ヘッダ) | アクセシビリティ情報インターフェース |
| UiaTracing.h | `src/types/UiaTracing.h` | ソース(ヘッダ) | UIAイベントのトレーシング |
