tor-browser

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

nsMaiInterfaceText.cpp (16238B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "InterfaceInitFuncs.h"
      8 #include "mozilla/a11y/PDocAccessible.h"
      9 #include "nsAccessibilityService.h"
     10 #include "LocalAccessible-inl.h"
     11 #include "HyperTextAccessible-inl.h"
     12 #include "nsMai.h"
     13 #include "RemoteAccessible.h"
     14 #include "AccAttributes.h"
     15 
     16 #include "nsIAccessibleTypes.h"
     17 #include "nsISimpleEnumerator.h"
     18 #include "nsUTF8Utils.h"
     19 
     20 #include "mozilla/Likely.h"
     21 
     22 #include "DOMtoATK.h"
     23 
     24 using namespace mozilla;
     25 using namespace mozilla::a11y;
     26 
     27 static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
     28 
     29 static AtkAttributeSet* ConvertToAtkTextAttributeSet(
     30    AccAttributes* aAttributes) {
     31  if (!aAttributes) {
     32    // This can happen if an Accessible dies in the content process, but the
     33    // parent hasn't been udpated yet.
     34    return nullptr;
     35  }
     36 
     37  AtkAttributeSet* atkAttributeSet = nullptr;
     38 
     39  for (auto iter : *aAttributes) {
     40    AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
     41    nsAutoString value;
     42    // We set atkAttr->name directly for each case. For the value, we set the
     43    // value string for each case. atkAttr->value is set at the end based on the
     44    // value string.
     45 
     46    // Set atkAttr->name to a specific ATK attribute name.
     47    auto atkName = [&atkAttr](AtkTextAttribute aAttrNum) {
     48      atkAttr->name = g_strdup(sAtkTextAttrNames[aAttrNum]);
     49    };
     50    // Set value to a formatted ATK color value.
     51    auto colorValue = [&iter, &value] {
     52      // The format of the atk attribute is r,g,b and the gecko one is
     53      // rgb(r, g, b).
     54      auto color = iter.Value<Color>();
     55      MOZ_ASSERT(color);
     56      value.AppendInt(NS_GET_R(color->mValue));
     57      value.Append(',');
     58      value.AppendInt(NS_GET_G(color->mValue));
     59      value.Append(',');
     60      value.AppendInt(NS_GET_B(color->mValue));
     61    };
     62 
     63    nsAtom* name = iter.Name();
     64    if (name == nsGkAtoms::color) {
     65      atkName(ATK_TEXT_ATTR_FG_COLOR);
     66      colorValue();
     67    } else if (name == nsGkAtoms::background_color) {
     68      atkName(ATK_TEXT_ATTR_BG_COLOR);
     69      colorValue();
     70    } else if (name == nsGkAtoms::font_family) {
     71      atkName(ATK_TEXT_ATTR_FAMILY_NAME);
     72      iter.ValueAsString(value);
     73    } else if (name == nsGkAtoms::font_size) {
     74      atkName(ATK_TEXT_ATTR_SIZE);
     75      // ATK wants the number of points without pt at the end.
     76      auto fontSize = iter.Value<FontSize>();
     77      MOZ_ASSERT(fontSize);
     78      value.AppendInt(fontSize->mValue);
     79    } else if (name == nsGkAtoms::font_weight) {
     80      atkName(ATK_TEXT_ATTR_WEIGHT);
     81      iter.ValueAsString(value);
     82    } else if (name == nsGkAtoms::invalid) {
     83      atkName(ATK_TEXT_ATTR_INVALID);
     84      iter.ValueAsString(value);
     85    } else {
     86      nsAutoString nameStr;
     87      iter.NameAsString(nameStr);
     88      atkAttr->name = g_strdup(NS_ConvertUTF16toUTF8(nameStr).get());
     89      iter.ValueAsString(value);
     90    }
     91 
     92    atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
     93    atkAttributeSet = g_slist_prepend(atkAttributeSet, atkAttr);
     94  }
     95 
     96  // libatk-adaptor will free it
     97  return atkAttributeSet;
     98 }
     99 
    100 extern "C" {
    101 
    102 static gchar* getTextCB(AtkText* aText, gint aStartOffset, gint aEndOffset) {
    103  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    104  if (!acc || !acc->IsTextRole()) {
    105    return nullptr;
    106  }
    107  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    108  if (!text) {
    109    return nullptr;
    110  }
    111  return DOMtoATK::NewATKString(text, aStartOffset, aEndOffset);
    112 }
    113 
    114 static gint getCharacterCountCB(AtkText* aText);
    115 
    116 // Note: this does not support magic offsets, which is fine for its callers
    117 // which do not implement any.
    118 static gchar* getCharTextAtOffset(AtkText* aText, gint aOffset,
    119                                  gint* aStartOffset, gint* aEndOffset) {
    120  gint end = aOffset + 1;
    121  gint count = getCharacterCountCB(aText);
    122 
    123  if (aOffset > count) {
    124    aOffset = count;
    125  }
    126  if (end > count) {
    127    end = count;
    128  }
    129  if (aOffset < 0) {
    130    aOffset = 0;
    131  }
    132  if (end < 0) {
    133    end = 0;
    134  }
    135  *aStartOffset = aOffset;
    136  *aEndOffset = end;
    137 
    138  return getTextCB(aText, aOffset, end);
    139 }
    140 
    141 static gchar* getTextAfterOffsetCB(AtkText* aText, gint aOffset,
    142                                   AtkTextBoundary aBoundaryType,
    143                                   gint* aStartOffset, gint* aEndOffset) {
    144  if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
    145    return getCharTextAtOffset(aText, aOffset + 1, aStartOffset, aEndOffset);
    146  }
    147 
    148  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    149  if (!acc) {
    150    return nullptr;
    151  }
    152 
    153  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    154  if (!text || !acc->IsTextRole()) {
    155    return nullptr;
    156  }
    157 
    158  nsAutoString autoStr;
    159  int32_t startOffset = 0, endOffset = 0;
    160  text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
    161                        autoStr);
    162 
    163  *aStartOffset = startOffset;
    164  *aEndOffset = endOffset;
    165 
    166  // libspi will free it.
    167  return DOMtoATK::Convert(autoStr);
    168 }
    169 
    170 static gchar* getTextAtOffsetCB(AtkText* aText, gint aOffset,
    171                                AtkTextBoundary aBoundaryType,
    172                                gint* aStartOffset, gint* aEndOffset) {
    173  if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
    174    return getCharTextAtOffset(aText, aOffset, aStartOffset, aEndOffset);
    175  }
    176 
    177  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    178  if (!acc) {
    179    return nullptr;
    180  }
    181 
    182  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    183  if (!text || !acc->IsTextRole()) {
    184    return nullptr;
    185  }
    186 
    187  nsAutoString autoStr;
    188  int32_t startOffset = 0, endOffset = 0;
    189  text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
    190 
    191  *aStartOffset = startOffset;
    192  *aEndOffset = endOffset;
    193 
    194  // libspi will free it.
    195  return DOMtoATK::Convert(autoStr);
    196 }
    197 
    198 static gunichar getCharacterAtOffsetCB(AtkText* aText, gint aOffset) {
    199  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    200  if (!acc) {
    201    return 0;
    202  }
    203 
    204  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    205  if (text) {
    206    return DOMtoATK::ATKCharacter(text, aOffset);
    207  }
    208 
    209  return 0;
    210 }
    211 
    212 static gchar* getTextBeforeOffsetCB(AtkText* aText, gint aOffset,
    213                                    AtkTextBoundary aBoundaryType,
    214                                    gint* aStartOffset, gint* aEndOffset) {
    215  if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) {
    216    return getCharTextAtOffset(aText, aOffset - 1, aStartOffset, aEndOffset);
    217  }
    218 
    219  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    220  if (!acc) {
    221    return nullptr;
    222  }
    223 
    224  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    225  if (!text || !acc->IsTextRole()) {
    226    return nullptr;
    227  }
    228 
    229  nsAutoString autoStr;
    230  int32_t startOffset = 0, endOffset = 0;
    231  text->TextBeforeOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
    232                         autoStr);
    233 
    234  *aStartOffset = startOffset;
    235  *aEndOffset = endOffset;
    236 
    237  // libspi will free it.
    238  return DOMtoATK::Convert(autoStr);
    239 }
    240 
    241 static gint getCaretOffsetCB(AtkText* aText) {
    242  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    243  if (!acc) {
    244    return -1;
    245  }
    246 
    247  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    248  if (!text || !acc->IsTextRole()) {
    249    return -1;
    250  }
    251 
    252  return static_cast<gint>(text->CaretOffset());
    253 }
    254 
    255 static AtkAttributeSet* getRunAttributesCB(AtkText* aText, gint aOffset,
    256                                           gint* aStartOffset,
    257                                           gint* aEndOffset) {
    258  *aStartOffset = -1;
    259  *aEndOffset = -1;
    260  int32_t startOffset = 0, endOffset = 0;
    261 
    262  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    263  if (!acc) {
    264    return nullptr;
    265  }
    266 
    267  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    268  if (!text || !acc->IsTextRole()) {
    269    return nullptr;
    270  }
    271 
    272  RefPtr<AccAttributes> attributes =
    273      text->TextAttributes(false, aOffset, &startOffset, &endOffset);
    274 
    275  *aStartOffset = startOffset;
    276  *aEndOffset = endOffset;
    277 
    278  return ConvertToAtkTextAttributeSet(attributes);
    279 }
    280 
    281 static AtkAttributeSet* getDefaultAttributesCB(AtkText* aText) {
    282  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    283  if (!acc) {
    284    return nullptr;
    285  }
    286 
    287  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    288  if (!text || !acc->IsTextRole()) {
    289    return nullptr;
    290  }
    291 
    292  RefPtr<AccAttributes> attributes = text->DefaultTextAttributes();
    293  return ConvertToAtkTextAttributeSet(attributes);
    294 }
    295 
    296 static void getCharacterExtentsCB(AtkText* aText, gint aOffset, gint* aX,
    297                                  gint* aY, gint* aWidth, gint* aHeight,
    298                                  AtkCoordType aCoords) {
    299  if (!aX || !aY || !aWidth || !aHeight) {
    300    return;
    301  }
    302  *aX = *aY = *aWidth = *aHeight = -1;
    303 
    304  uint32_t geckoCoordType;
    305  if (aCoords == ATK_XY_SCREEN) {
    306    geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
    307  } else {
    308    geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
    309  }
    310 
    311  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    312  if (!acc) {
    313    return;
    314  }
    315 
    316  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    317  if (!text || !acc->IsTextRole()) {
    318    return;
    319  }
    320 
    321  LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType);
    322 
    323  *aX = rect.x;
    324  *aY = rect.y;
    325  *aWidth = rect.width;
    326  *aHeight = rect.height;
    327 }
    328 
    329 static void getRangeExtentsCB(AtkText* aText, gint aStartOffset,
    330                              gint aEndOffset, AtkCoordType aCoords,
    331                              AtkTextRectangle* aRect) {
    332  if (!aRect) {
    333    return;
    334  }
    335  aRect->x = aRect->y = aRect->width = aRect->height = -1;
    336 
    337  uint32_t geckoCoordType;
    338  if (aCoords == ATK_XY_SCREEN) {
    339    geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
    340  } else {
    341    geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
    342  }
    343 
    344  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    345  if (!acc) {
    346    return;
    347  }
    348 
    349  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    350  if (!text || !acc->IsTextRole()) {
    351    return;
    352  }
    353 
    354  LayoutDeviceIntRect rect =
    355      text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
    356 
    357  aRect->x = rect.x;
    358  aRect->y = rect.y;
    359  aRect->width = rect.width;
    360  aRect->height = rect.height;
    361 }
    362 
    363 static gint getCharacterCountCB(AtkText* aText) {
    364  if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) {
    365    if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) {
    366      return static_cast<gint>(text->CharacterCount());
    367    }
    368  }
    369  return 0;
    370 }
    371 
    372 static gint getOffsetAtPointCB(AtkText* aText, gint aX, gint aY,
    373                               AtkCoordType aCoords) {
    374  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    375  if (!acc) {
    376    return -1;
    377  }
    378  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    379  if (!text || !acc->IsTextRole()) {
    380    return -1;
    381  }
    382  return static_cast<gint>(text->OffsetAtPoint(
    383      aX, aY,
    384      (aCoords == ATK_XY_SCREEN
    385           ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
    386           : nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
    387 }
    388 
    389 static gint getTextSelectionCountCB(AtkText* aText) {
    390  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    391  if (!acc) {
    392    return 0;
    393  }
    394 
    395  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    396  if (!text || !acc->IsTextRole()) {
    397    return 0;
    398  }
    399 
    400  return text->SelectionCount();
    401 }
    402 
    403 static gchar* getTextSelectionCB(AtkText* aText, gint aSelectionNum,
    404                                 gint* aStartOffset, gint* aEndOffset) {
    405  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    406  if (!acc) {
    407    return nullptr;
    408  }
    409 
    410  int32_t startOffset = 0, endOffset = 0;
    411  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    412  if (!text || !acc->IsTextRole()) {
    413    return nullptr;
    414  }
    415 
    416  text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
    417  *aStartOffset = startOffset;
    418  *aEndOffset = endOffset;
    419 
    420  return getTextCB(aText, *aStartOffset, *aEndOffset);
    421 }
    422 
    423 // set methods
    424 static gboolean addTextSelectionCB(AtkText* aText, gint aStartOffset,
    425                                   gint aEndOffset) {
    426  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
    427  if (accWrap) {
    428    HyperTextAccessible* text = accWrap->AsHyperText();
    429    if (!text || !text->IsTextRole()) {
    430      return FALSE;
    431    }
    432 
    433    return text->AddToSelection(aStartOffset, aEndOffset);
    434  }
    435  if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
    436    return proxy->AddToSelection(aStartOffset, aEndOffset);
    437  }
    438 
    439  return FALSE;
    440 }
    441 
    442 static gboolean removeTextSelectionCB(AtkText* aText, gint aSelectionNum) {
    443  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
    444  if (accWrap) {
    445    HyperTextAccessible* text = accWrap->AsHyperText();
    446    if (!text || !text->IsTextRole()) {
    447      return FALSE;
    448    }
    449 
    450    return text->RemoveFromSelection(aSelectionNum);
    451  }
    452  if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
    453    return proxy->RemoveFromSelection(aSelectionNum);
    454  }
    455 
    456  return FALSE;
    457 }
    458 
    459 static gboolean setTextSelectionCB(AtkText* aText, gint aSelectionNum,
    460                                   gint aStartOffset, gint aEndOffset) {
    461  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    462  if (!acc || !acc->IsTextRole()) {
    463    return FALSE;
    464  }
    465  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    466  if (!text) {
    467    return FALSE;
    468  }
    469  return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
    470 }
    471 
    472 static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) {
    473  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    474  if (!acc) {
    475    return FALSE;
    476  }
    477 
    478  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    479  if (!text || !acc->IsTextRole()) {
    480    return FALSE;
    481  }
    482 
    483  text->SetCaretOffset(aOffset);
    484  return TRUE;
    485 }
    486 
    487 static gboolean scrollSubstringToCB(AtkText* aText, gint aStartOffset,
    488                                    gint aEndOffset, AtkScrollType aType) {
    489  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    490  if (!acc) {
    491    return FALSE;
    492  }
    493 
    494  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    495  if (!text) {
    496    return FALSE;
    497  }
    498 
    499  text->ScrollSubstringTo(aStartOffset, aEndOffset, aType);
    500 
    501  return TRUE;
    502 }
    503 
    504 static gboolean scrollSubstringToPointCB(AtkText* aText, gint aStartOffset,
    505                                         gint aEndOffset, AtkCoordType aCoords,
    506                                         gint aX, gint aY) {
    507  Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
    508  if (!acc) {
    509    return FALSE;
    510  }
    511 
    512  HyperTextAccessibleBase* text = acc->AsHyperTextBase();
    513  if (!text) {
    514    return FALSE;
    515  }
    516 
    517  text->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY);
    518  return TRUE;
    519 }
    520 }
    521 
    522 void textInterfaceInitCB(AtkTextIface* aIface) {
    523  NS_ASSERTION(aIface, "Invalid aIface");
    524  if (MOZ_UNLIKELY(!aIface)) return;
    525 
    526  aIface->get_text = getTextCB;
    527  aIface->get_text_after_offset = getTextAfterOffsetCB;
    528  aIface->get_text_at_offset = getTextAtOffsetCB;
    529  aIface->get_character_at_offset = getCharacterAtOffsetCB;
    530  aIface->get_text_before_offset = getTextBeforeOffsetCB;
    531  aIface->get_caret_offset = getCaretOffsetCB;
    532  aIface->get_run_attributes = getRunAttributesCB;
    533  aIface->get_default_attributes = getDefaultAttributesCB;
    534  aIface->get_character_extents = getCharacterExtentsCB;
    535  aIface->get_range_extents = getRangeExtentsCB;
    536  aIface->get_character_count = getCharacterCountCB;
    537  aIface->get_offset_at_point = getOffsetAtPointCB;
    538  aIface->get_n_selections = getTextSelectionCountCB;
    539  aIface->get_selection = getTextSelectionCB;
    540 
    541  // set methods
    542  aIface->add_selection = addTextSelectionCB;
    543  aIface->remove_selection = removeTextSelectionCB;
    544  aIface->set_selection = setTextSelectionCB;
    545  aIface->set_caret_offset = setCaretOffsetCB;
    546 
    547  if (IsAtkVersionAtLeast(2, 32)) {
    548    aIface->scroll_substring_to = scrollSubstringToCB;
    549    aIface->scroll_substring_to_point = scrollSubstringToPointCB;
    550  }
    551 
    552  // Cache the string values of the atk text attribute names.
    553  for (uint32_t i = 0; i < std::size(sAtkTextAttrNames); i++) {
    554    sAtkTextAttrNames[i] =
    555        atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
    556  }
    557 }