tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UiaText.cpp (5617B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "UiaText.h"
      8 
      9 #include "MsaaAccessible.h"
     10 #include "mozilla/a11y/States.h"
     11 #include "TextLeafRange.h"
     12 #include "UiaTextRange.h"
     13 
     14 namespace mozilla::a11y {
     15 
     16 // Helpers
     17 
     18 static SAFEARRAY* TextLeafRangesToUiaRanges(
     19    const nsTArray<TextLeafRange>& aRanges) {
     20  // The documentation for GetSelection doesn't specify whether we should return
     21  // an empty array or null if there are no ranges to return. However,
     22  // GetVisibleRanges says that we should return an empty array, never null, so
     23  // that's what we do.
     24  // https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcore/nf-uiautomationcore-itextprovider-getvisibleranges
     25  SAFEARRAY* uiaRanges = SafeArrayCreateVector(VT_UNKNOWN, 0, aRanges.Length());
     26  LONG indices[1] = {0};
     27  for (const TextLeafRange& range : aRanges) {
     28    // SafeArrayPutElement calls AddRef on the element, so we use a raw
     29    // pointer here.
     30    UiaTextRange* uiaRange = new UiaTextRange(range);
     31    SafeArrayPutElement(uiaRanges, indices, uiaRange);
     32    ++indices[0];
     33  }
     34  return uiaRanges;
     35 }
     36 
     37 // IUnknown
     38 IMPL_IUNKNOWN1(UiaText, ITextProvider)
     39 
     40 // UiaText
     41 
     42 UiaText::UiaText(MsaaAccessible* aMsaa) : mMsaa(aMsaa) {}
     43 
     44 Accessible* UiaText::Acc() const { return mMsaa->Acc(); }
     45 
     46 // ITextProvider methods
     47 
     48 STDMETHODIMP
     49 UiaText::GetSelection(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
     50  if (!aRetVal) {
     51    return E_INVALIDARG;
     52  }
     53  Accessible* acc = Acc();
     54  if (!acc) {
     55    return CO_E_OBJNOTCONNECTED;
     56  }
     57  AutoTArray<TextLeafRange, 1> ranges;
     58  TextLeafRange::GetSelection(acc, ranges);
     59  if (ranges.IsEmpty()) {
     60    // There is no selection. Check if there is a caret.
     61    if (TextLeafPoint caret = TextLeafPoint::GetCaret(acc)) {
     62      ranges.EmplaceBack(caret, caret);
     63    }
     64  }
     65  *aRetVal = TextLeafRangesToUiaRanges(ranges);
     66  return S_OK;
     67 }
     68 
     69 STDMETHODIMP
     70 UiaText::GetVisibleRanges(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
     71  if (!aRetVal) {
     72    return E_INVALIDARG;
     73  }
     74  Accessible* acc = Acc();
     75  if (!acc) {
     76    return CO_E_OBJNOTCONNECTED;
     77  }
     78  TextLeafRange fullRange = TextLeafRange::FromAccessible(acc);
     79  // The most pragmatic way to determine visible text is to walk by line.
     80  // XXX TextLeafRange::VisibleLines doesn't correctly handle lines that are
     81  // scrolled out where the scroll container is a descendant of acc. See bug
     82  // 1945010.
     83  nsTArray<TextLeafRange> ranges = fullRange.VisibleLines(acc);
     84  *aRetVal = TextLeafRangesToUiaRanges(ranges);
     85  return S_OK;
     86 }
     87 
     88 STDMETHODIMP
     89 UiaText::RangeFromChild(__RPC__in_opt IRawElementProviderSimple* aChildElement,
     90                        __RPC__deref_out_opt ITextRangeProvider** aRetVal) {
     91  if (!aChildElement || !aRetVal) {
     92    return E_INVALIDARG;
     93  }
     94  *aRetVal = nullptr;
     95  Accessible* acc = Acc();
     96  if (!acc) {
     97    return CO_E_OBJNOTCONNECTED;
     98  }
     99  Accessible* child = MsaaAccessible::GetAccessibleFrom(aChildElement);
    100  if (!child || !acc->IsAncestorOf(child)) {
    101    return E_INVALIDARG;
    102  }
    103  TextLeafRange range = TextLeafRange::FromAccessible(child);
    104  RefPtr uiaRange = new UiaTextRange(range);
    105  uiaRange.forget(aRetVal);
    106  return S_OK;
    107 }
    108 
    109 STDMETHODIMP
    110 UiaText::RangeFromPoint(struct UiaPoint aPoint,
    111                        __RPC__deref_out_opt ITextRangeProvider** aRetVal) {
    112  if (!aRetVal) {
    113    return E_INVALIDARG;
    114  }
    115  *aRetVal = nullptr;
    116  Accessible* acc = Acc();
    117  if (!acc) {
    118    return CO_E_OBJNOTCONNECTED;
    119  }
    120 
    121  // Find the deepest accessible node at the given screen coordinates.
    122  Accessible* child = acc->ChildAtPoint(
    123      aPoint.x, aPoint.y, Accessible::EWhichChildAtPoint::DeepestChild);
    124  if (!child) {
    125    return E_INVALIDARG;
    126  }
    127 
    128  // Find the closest point within the entirety of the leaf where the screen
    129  // coordinates lie.
    130  TextLeafRange leafRange = TextLeafRange::FromAccessible(child);
    131  TextLeafPoint closestPoint =
    132      leafRange.TextLeafPointAtScreenPoint(aPoint.x, aPoint.y);
    133  TextLeafRange range{closestPoint, closestPoint};
    134  RefPtr uiaRange = new UiaTextRange(range);
    135  uiaRange.forget(aRetVal);
    136  return S_OK;
    137 }
    138 
    139 STDMETHODIMP
    140 UiaText::get_DocumentRange(__RPC__deref_out_opt ITextRangeProvider** aRetVal) {
    141  if (!aRetVal) {
    142    return E_INVALIDARG;
    143  }
    144  Accessible* acc = Acc();
    145  if (!acc) {
    146    return CO_E_OBJNOTCONNECTED;
    147  }
    148  // On the web, the "document range" could either span the entire document or
    149  // just a text input control, depending on the element on which the Text
    150  // pattern was queried. See:
    151  // https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-textpattern-and-embedded-objects-overview#webpage-and-text-input-controls-in-edge
    152  TextLeafRange range = TextLeafRange::FromAccessible(acc);
    153  RefPtr uiaRange = new UiaTextRange(range);
    154  uiaRange.forget(aRetVal);
    155  return S_OK;
    156 }
    157 
    158 STDMETHODIMP
    159 UiaText::get_SupportedTextSelection(
    160    __RPC__out enum SupportedTextSelection* aRetVal) {
    161  if (!aRetVal) {
    162    return E_INVALIDARG;
    163  }
    164  Accessible* acc = Acc();
    165  if (!acc) {
    166    return CO_E_OBJNOTCONNECTED;
    167  }
    168  if (!acc->IsHyperText()) {
    169    // Currently, the SELECTABLE_TEXT state is only exposed on HyperText
    170    // Accessibles.
    171    acc = acc->Parent();
    172  }
    173  if (acc && acc->State() & states::SELECTABLE_TEXT) {
    174    *aRetVal = SupportedTextSelection_Multiple;
    175  } else {
    176    *aRetVal = SupportedTextSelection_None;
    177  }
    178  return S_OK;
    179 }
    180 
    181 }  // namespace mozilla::a11y