tor-browser

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

ia2AccessibleText.cpp (14597B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:expandtab:shiftwidth=2:tabstop=2:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "ia2Accessible.h"
      9 #include "ia2AccessibleHypertext.h"
     10 #include "ia2AccessibleText.h"
     11 
     12 #include "AccessibleText_i.c"
     13 
     14 #include "mozilla/a11y/Compatibility.h"
     15 #include "mozilla/a11y/HyperTextAccessibleBase.h"
     16 #include "mozilla/ClearOnShutdown.h"
     17 
     18 using namespace mozilla::a11y;
     19 
     20 HyperTextAccessibleBase* ia2AccessibleText::sLastTextChangeAcc = nullptr;
     21 mozilla::StaticAutoPtr<nsString> ia2AccessibleText::sLastTextChangeString;
     22 uint32_t ia2AccessibleText::sLastTextChangeStart = 0;
     23 uint32_t ia2AccessibleText::sLastTextChangeEnd = 0;
     24 bool ia2AccessibleText::sLastTextChangeWasInsert = false;
     25 
     26 HyperTextAccessibleBase* ia2AccessibleText::TextAcc() {
     27  auto hyp = static_cast<ia2AccessibleHypertext*>(this);
     28  Accessible* acc = hyp->Acc();
     29  return acc ? acc->AsHyperTextBase() : nullptr;
     30 }
     31 
     32 // IAccessibleText
     33 
     34 STDMETHODIMP
     35 ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset) {
     36  HyperTextAccessibleBase* textAcc = TextAcc();
     37  if (!textAcc) {
     38    return CO_E_OBJNOTCONNECTED;
     39  }
     40 
     41  return textAcc->AddToSelection(aStartOffset, aEndOffset) ? S_OK
     42                                                           : E_INVALIDARG;
     43 }
     44 
     45 STDMETHODIMP
     46 ia2AccessibleText::get_attributes(long aOffset, long* aStartOffset,
     47                                  long* aEndOffset, BSTR* aTextAttributes) {
     48  if (!aStartOffset || !aEndOffset || !aTextAttributes) return E_INVALIDARG;
     49 
     50  *aStartOffset = 0;
     51  *aEndOffset = 0;
     52  *aTextAttributes = nullptr;
     53 
     54  int32_t startOffset = 0, endOffset = 0;
     55  HyperTextAccessibleBase* textAcc = TextAcc();
     56  if (!textAcc) {
     57    return CO_E_OBJNOTCONNECTED;
     58  }
     59 
     60  RefPtr<AccAttributes> attributes =
     61      textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset);
     62 
     63  HRESULT hr =
     64      ia2Accessible::ConvertToIA2Attributes(attributes, aTextAttributes);
     65  if (FAILED(hr)) return hr;
     66 
     67  *aStartOffset = startOffset;
     68  *aEndOffset = endOffset;
     69 
     70  return S_OK;
     71 }
     72 
     73 STDMETHODIMP
     74 ia2AccessibleText::get_caretOffset(long* aOffset) {
     75  if (!aOffset) return E_INVALIDARG;
     76 
     77  *aOffset = -1;
     78 
     79  HyperTextAccessibleBase* textAcc = TextAcc();
     80  if (!textAcc) {
     81    return CO_E_OBJNOTCONNECTED;
     82  }
     83 
     84  *aOffset = textAcc->CaretOffset();
     85 
     86  return *aOffset != -1 ? S_OK : S_FALSE;
     87 }
     88 
     89 STDMETHODIMP
     90 ia2AccessibleText::get_characterExtents(long aOffset,
     91                                        enum IA2CoordinateType aCoordType,
     92                                        long* aX, long* aY, long* aWidth,
     93                                        long* aHeight) {
     94  if (!aX || !aY || !aWidth || !aHeight) return E_INVALIDARG;
     95  *aX = *aY = *aWidth = *aHeight = 0;
     96 
     97  uint32_t geckoCoordType =
     98      (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE)
     99          ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
    100          : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
    101  LayoutDeviceIntRect rect;
    102  auto textAcc = TextAcc();
    103  if (!textAcc) {
    104    return CO_E_OBJNOTCONNECTED;
    105  }
    106 
    107  rect = textAcc->CharBounds(aOffset, geckoCoordType);
    108 
    109  // Can't use GetRect() because of long vs. int32_t mismatch
    110  *aX = rect.X();
    111  *aY = rect.Y();
    112  *aWidth = rect.Width();
    113  *aHeight = rect.Height();
    114  return S_OK;
    115 }
    116 
    117 STDMETHODIMP
    118 ia2AccessibleText::get_nSelections(long* aNSelections) {
    119  if (!aNSelections) return E_INVALIDARG;
    120  *aNSelections = 0;
    121 
    122  HyperTextAccessibleBase* textAcc = TextAcc();
    123  if (!textAcc) {
    124    return CO_E_OBJNOTCONNECTED;
    125  }
    126 
    127  *aNSelections = textAcc->SelectionCount();
    128  if (*aNSelections == 0 &&
    129      (Compatibility::A11ySuppressionReasons() &
    130       SuppressionReasons::Clipboard) &&
    131      static_cast<ia2AccessibleHypertext*>(this)->Acc()->IsDoc()) {
    132    // Bug 1798098: Windows Suggested Actions (introduced in Windows 11
    133    // 22H2) might walk the document a11y tree using UIA whenever anything
    134    // is copied to the clipboard. This causes an unacceptable hang. It walks
    135    // using IAccessibleText/IAccessibleHyperText if the document reports no
    136    // selection, so we lie here and say that there is a selection even though
    137    // there isn't. It will subsequently call get_selection, which will fail,
    138    // but this hack here seems to be enough to avoid further text calls.
    139    *aNSelections = 1;
    140  }
    141 
    142  return S_OK;
    143 }
    144 
    145 STDMETHODIMP
    146 ia2AccessibleText::get_offsetAtPoint(long aX, long aY,
    147                                     enum IA2CoordinateType aCoordType,
    148                                     long* aOffset) {
    149  if (!aOffset) return E_INVALIDARG;
    150  *aOffset = 0;
    151 
    152  uint32_t geckoCoordType =
    153      (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE)
    154          ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
    155          : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
    156 
    157  HyperTextAccessibleBase* textAcc = TextAcc();
    158  if (!textAcc) {
    159    return CO_E_OBJNOTCONNECTED;
    160  }
    161 
    162  *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType);
    163 
    164  return *aOffset == -1 ? S_FALSE : S_OK;
    165 }
    166 
    167 STDMETHODIMP
    168 ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset,
    169                                 long* aEndOffset) {
    170  if (!aStartOffset || !aEndOffset) return E_INVALIDARG;
    171  *aStartOffset = *aEndOffset = 0;
    172 
    173  int32_t startOffset = 0, endOffset = 0;
    174  HyperTextAccessibleBase* textAcc = TextAcc();
    175  if (!textAcc) {
    176    return CO_E_OBJNOTCONNECTED;
    177  }
    178 
    179  if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset)) {
    180    return E_INVALIDARG;
    181  }
    182 
    183  *aStartOffset = startOffset;
    184  *aEndOffset = endOffset;
    185  return S_OK;
    186 }
    187 
    188 STDMETHODIMP
    189 ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText) {
    190  if (!aText) return E_INVALIDARG;
    191 
    192  *aText = nullptr;
    193 
    194  nsAutoString text;
    195  HyperTextAccessibleBase* textAcc = TextAcc();
    196  if (!textAcc) {
    197    return CO_E_OBJNOTCONNECTED;
    198  }
    199 
    200  if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) {
    201    return E_INVALIDARG;
    202  }
    203 
    204  textAcc->TextSubstring(aStartOffset, aEndOffset, text);
    205 
    206  if (text.IsEmpty()) return S_FALSE;
    207 
    208  *aText = ::SysAllocStringLen(text.get(), text.Length());
    209  return *aText ? S_OK : E_OUTOFMEMORY;
    210 }
    211 
    212 STDMETHODIMP
    213 ia2AccessibleText::get_textBeforeOffset(long aOffset,
    214                                        enum IA2TextBoundaryType aBoundaryType,
    215                                        long* aStartOffset, long* aEndOffset,
    216                                        BSTR* aText) {
    217  if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG;
    218 
    219  *aStartOffset = *aEndOffset = 0;
    220  *aText = nullptr;
    221 
    222  HyperTextAccessibleBase* textAcc = TextAcc();
    223  if (!textAcc) {
    224    return CO_E_OBJNOTCONNECTED;
    225  }
    226 
    227  if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG;
    228 
    229  nsAutoString text;
    230  int32_t startOffset = 0, endOffset = 0;
    231 
    232  if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
    233    startOffset = 0;
    234    endOffset = textAcc->CharacterCount();
    235    textAcc->TextSubstring(startOffset, endOffset, text);
    236  } else {
    237    AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
    238    if (boundaryType == -1) return S_FALSE;
    239 
    240    textAcc->TextBeforeOffset(aOffset, boundaryType, &startOffset, &endOffset,
    241                              text);
    242  }
    243 
    244  *aStartOffset = startOffset;
    245  *aEndOffset = endOffset;
    246 
    247  if (text.IsEmpty()) return S_FALSE;
    248 
    249  *aText = ::SysAllocStringLen(text.get(), text.Length());
    250  return *aText ? S_OK : E_OUTOFMEMORY;
    251 }
    252 
    253 STDMETHODIMP
    254 ia2AccessibleText::get_textAfterOffset(long aOffset,
    255                                       enum IA2TextBoundaryType aBoundaryType,
    256                                       long* aStartOffset, long* aEndOffset,
    257                                       BSTR* aText) {
    258  if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG;
    259 
    260  *aStartOffset = 0;
    261  *aEndOffset = 0;
    262  *aText = nullptr;
    263 
    264  HyperTextAccessibleBase* textAcc = TextAcc();
    265  if (!textAcc) {
    266    return CO_E_OBJNOTCONNECTED;
    267  }
    268 
    269  if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG;
    270 
    271  nsAutoString text;
    272  int32_t startOffset = 0, endOffset = 0;
    273 
    274  if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
    275    startOffset = 0;
    276    endOffset = textAcc->CharacterCount();
    277    textAcc->TextSubstring(startOffset, endOffset, text);
    278  } else {
    279    AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
    280    if (boundaryType == -1) return S_FALSE;
    281    textAcc->TextAfterOffset(aOffset, boundaryType, &startOffset, &endOffset,
    282                             text);
    283  }
    284 
    285  *aStartOffset = startOffset;
    286  *aEndOffset = endOffset;
    287 
    288  if (text.IsEmpty()) return S_FALSE;
    289 
    290  *aText = ::SysAllocStringLen(text.get(), text.Length());
    291  return *aText ? S_OK : E_OUTOFMEMORY;
    292 }
    293 
    294 STDMETHODIMP
    295 ia2AccessibleText::get_textAtOffset(long aOffset,
    296                                    enum IA2TextBoundaryType aBoundaryType,
    297                                    long* aStartOffset, long* aEndOffset,
    298                                    BSTR* aText) {
    299  if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG;
    300 
    301  *aStartOffset = *aEndOffset = 0;
    302  *aText = nullptr;
    303 
    304  HyperTextAccessibleBase* textAcc = TextAcc();
    305  if (!textAcc) return CO_E_OBJNOTCONNECTED;
    306 
    307  if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG;
    308 
    309  nsAutoString text;
    310  int32_t startOffset = 0, endOffset = 0;
    311  if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
    312    startOffset = 0;
    313    endOffset = textAcc->CharacterCount();
    314    textAcc->TextSubstring(startOffset, endOffset, text);
    315  } else {
    316    AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
    317    if (boundaryType == -1) return S_FALSE;
    318    textAcc->TextAtOffset(aOffset, boundaryType, &startOffset, &endOffset,
    319                          text);
    320  }
    321 
    322  *aStartOffset = startOffset;
    323  *aEndOffset = endOffset;
    324 
    325  if (text.IsEmpty()) return S_FALSE;
    326 
    327  *aText = ::SysAllocStringLen(text.get(), text.Length());
    328  return *aText ? S_OK : E_OUTOFMEMORY;
    329 }
    330 
    331 STDMETHODIMP
    332 ia2AccessibleText::removeSelection(long aSelectionIndex) {
    333  HyperTextAccessibleBase* textAcc = TextAcc();
    334  if (!textAcc) {
    335    return CO_E_OBJNOTCONNECTED;
    336  }
    337 
    338  return textAcc->RemoveFromSelection(aSelectionIndex) ? S_OK : E_INVALIDARG;
    339 }
    340 
    341 STDMETHODIMP
    342 ia2AccessibleText::setCaretOffset(long aOffset) {
    343  HyperTextAccessibleBase* textAcc = TextAcc();
    344  if (!textAcc) {
    345    return CO_E_OBJNOTCONNECTED;
    346  }
    347 
    348  if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG;
    349 
    350  textAcc->SetCaretOffset(aOffset);
    351  return S_OK;
    352 }
    353 
    354 STDMETHODIMP
    355 ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset,
    356                                long aEndOffset) {
    357  HyperTextAccessibleBase* textAcc = TextAcc();
    358  if (!textAcc) {
    359    return CO_E_OBJNOTCONNECTED;
    360  }
    361 
    362  return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset,
    363                                       aEndOffset)
    364             ? S_OK
    365             : E_INVALIDARG;
    366 }
    367 
    368 STDMETHODIMP
    369 ia2AccessibleText::get_nCharacters(long* aNCharacters) {
    370  if (!aNCharacters) return E_INVALIDARG;
    371  *aNCharacters = 0;
    372 
    373  HyperTextAccessibleBase* textAcc = TextAcc();
    374  if (!textAcc) return CO_E_OBJNOTCONNECTED;
    375 
    376  *aNCharacters = textAcc->CharacterCount();
    377  return S_OK;
    378 }
    379 
    380 STDMETHODIMP
    381 ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
    382                                     enum IA2ScrollType aScrollType) {
    383  HyperTextAccessibleBase* textAcc = TextAcc();
    384  if (!textAcc) {
    385    return CO_E_OBJNOTCONNECTED;
    386  }
    387 
    388  if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) return E_INVALIDARG;
    389 
    390  textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType);
    391  return S_OK;
    392 }
    393 
    394 STDMETHODIMP
    395 ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex,
    396                                          enum IA2CoordinateType aCoordType,
    397                                          long aX, long aY) {
    398  HyperTextAccessibleBase* textAcc = TextAcc();
    399  if (!textAcc) {
    400    return CO_E_OBJNOTCONNECTED;
    401  }
    402  if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) {
    403    return E_INVALIDARG;
    404  }
    405  uint32_t geckoCoordType =
    406      (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE)
    407          ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
    408          : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
    409  textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex, geckoCoordType, aX,
    410                                  aY);
    411  return S_OK;
    412 }
    413 
    414 STDMETHODIMP
    415 ia2AccessibleText::get_newText(IA2TextSegment* aNewText) {
    416  return GetModifiedText(true, aNewText);
    417 }
    418 
    419 STDMETHODIMP
    420 ia2AccessibleText::get_oldText(IA2TextSegment* aOldText) {
    421  return GetModifiedText(false, aOldText);
    422 }
    423 
    424 // ia2AccessibleText
    425 
    426 HRESULT
    427 ia2AccessibleText::GetModifiedText(bool aGetInsertedText,
    428                                   IA2TextSegment* aText) {
    429  if (!aText) return E_INVALIDARG;
    430 
    431  if (!sLastTextChangeAcc) return S_OK;
    432 
    433  if (aGetInsertedText != sLastTextChangeWasInsert) return S_OK;
    434 
    435  if (sLastTextChangeAcc != TextAcc()) return S_OK;
    436 
    437  aText->start = sLastTextChangeStart;
    438  aText->end = sLastTextChangeEnd;
    439 
    440  if (sLastTextChangeString->IsEmpty()) return S_FALSE;
    441 
    442  aText->text = ::SysAllocStringLen(sLastTextChangeString->get(),
    443                                    sLastTextChangeString->Length());
    444  return aText->text ? S_OK : E_OUTOFMEMORY;
    445 }
    446 
    447 AccessibleTextBoundary ia2AccessibleText::GetGeckoTextBoundary(
    448    enum IA2TextBoundaryType aBoundaryType) {
    449  switch (aBoundaryType) {
    450    case IA2_TEXT_BOUNDARY_CHAR:
    451      return nsIAccessibleText::BOUNDARY_CLUSTER;
    452    case IA2_TEXT_BOUNDARY_WORD:
    453      return nsIAccessibleText::BOUNDARY_WORD_START;
    454    case IA2_TEXT_BOUNDARY_LINE:
    455      return nsIAccessibleText::BOUNDARY_LINE_START;
    456    case IA2_TEXT_BOUNDARY_PARAGRAPH:
    457      return nsIAccessibleText::BOUNDARY_PARAGRAPH;
    458    // case IA2_TEXT_BOUNDARY_SENTENCE:
    459    // XXX: not implemented
    460    default:
    461      return -1;
    462  }
    463 }
    464 
    465 void ia2AccessibleText::InitTextChangeData() {
    466  ClearOnShutdown(&sLastTextChangeString);
    467 }
    468 
    469 void ia2AccessibleText::UpdateTextChangeData(HyperTextAccessibleBase* aAcc,
    470                                             bool aInsert,
    471                                             const nsAString& aStr,
    472                                             int32_t aStart, uint32_t aLen) {
    473  if (!sLastTextChangeString) sLastTextChangeString = new nsString();
    474 
    475  sLastTextChangeAcc = aAcc;
    476  sLastTextChangeStart = aStart;
    477  sLastTextChangeEnd = aStart + aLen;
    478  sLastTextChangeWasInsert = aInsert;
    479  *sLastTextChangeString = aStr;
    480 }