tor-browser

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

GeckoTextMarker.mm (19043B)


      1 /* clang-format off */
      2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      3 /* clang-format on */
      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 #import "GeckoTextMarker.h"
      9 
     10 #import "MacUtils.h"
     11 
     12 #include "AccAttributes.h"
     13 #include "DocAccessible.h"
     14 #include "DocAccessibleParent.h"
     15 #include "nsCocoaUtils.h"
     16 #include "HyperTextAccessible.h"
     17 #include "States.h"
     18 #include "nsAccUtils.h"
     19 
     20 namespace mozilla {
     21 namespace a11y {
     22 
     23 struct TextMarkerData {
     24  TextMarkerData(uintptr_t aDoc, uintptr_t aID, int32_t aOffset)
     25      : mDoc(aDoc), mID(aID), mOffset(aOffset) {}
     26  TextMarkerData() {}
     27  uintptr_t mDoc;
     28  uintptr_t mID;
     29  int32_t mOffset;
     30 };
     31 
     32 // GeckoTextMarker
     33 
     34 GeckoTextMarker::GeckoTextMarker(Accessible* aAcc, int32_t aOffset) {
     35  HyperTextAccessibleBase* ht = aAcc->AsHyperTextBase();
     36  if (ht && aOffset != nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT &&
     37      aOffset <= static_cast<int32_t>(ht->CharacterCount())) {
     38    mPoint = aAcc->AsHyperTextBase()->ToTextLeafPoint(aOffset);
     39  } else {
     40    mPoint = TextLeafPoint(aAcc, aOffset);
     41  }
     42 }
     43 
     44 GeckoTextMarker GeckoTextMarker::MarkerFromAXTextMarker(
     45    Accessible* aDoc, AXTextMarkerRef aTextMarker) {
     46  MOZ_ASSERT(aDoc);
     47  if (!aTextMarker) {
     48    return GeckoTextMarker();
     49  }
     50 
     51  if (AXTextMarkerGetLength(aTextMarker) != sizeof(TextMarkerData)) {
     52    MOZ_ASSERT_UNREACHABLE("Malformed AXTextMarkerRef");
     53    return GeckoTextMarker();
     54  }
     55 
     56  TextMarkerData markerData;
     57  memcpy(&markerData, AXTextMarkerGetBytePtr(aTextMarker),
     58         sizeof(TextMarkerData));
     59 
     60  if (!utils::DocumentExists(aDoc, markerData.mDoc)) {
     61    return GeckoTextMarker();
     62  }
     63 
     64  Accessible* doc = reinterpret_cast<Accessible*>(markerData.mDoc);
     65  MOZ_ASSERT(doc->IsDoc());
     66  int32_t offset = markerData.mOffset;
     67  Accessible* acc = nullptr;
     68  if (doc->IsRemote()) {
     69    acc = doc->AsRemote()->AsDoc()->GetAccessible(markerData.mID);
     70  } else {
     71    acc = doc->AsLocal()->AsDoc()->GetAccessibleByUniqueID(
     72        reinterpret_cast<void*>(markerData.mID));
     73  }
     74 
     75  if (!acc) {
     76    return GeckoTextMarker();
     77  }
     78 
     79  return GeckoTextMarker(acc, offset);
     80 }
     81 
     82 GeckoTextMarker GeckoTextMarker::MarkerFromIndex(Accessible* aRoot,
     83                                                 int32_t aIndex) {
     84  TextLeafRange range(
     85      TextLeafPoint(aRoot, 0),
     86      TextLeafPoint(aRoot, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT));
     87  int32_t index = aIndex;
     88  // Iterate through all segments until we exhausted the index sum
     89  // so we can find the segment the index lives in.
     90  for (TextLeafRange segment : range) {
     91    if (segment.Start().mAcc->IsMenuPopup() &&
     92        (segment.Start().mAcc->State() & states::INVISIBLE)) {
     93      // XXX: Invisible XUL menu popups are in our tree and we need to skip
     94      // them.
     95      continue;
     96    }
     97 
     98    if (segment.End().mAcc->Role() == roles::LISTITEM_MARKER) {
     99      // XXX: MacOS expects bullets to be in the range's text, but not in
    100      // the calculated length!
    101      continue;
    102    }
    103 
    104    index -= segment.End().mOffset - segment.Start().mOffset;
    105    if (index <= 0) {
    106      // The index is in the current segment.
    107      return GeckoTextMarker(segment.Start().mAcc,
    108                             segment.End().mOffset + index);
    109    }
    110  }
    111 
    112  return GeckoTextMarker();
    113 }
    114 
    115 AXTextMarkerRef GeckoTextMarker::CreateAXTextMarker() {
    116  if (!IsValid()) {
    117    return nil;
    118  }
    119 
    120  Accessible* doc = nsAccUtils::DocumentFor(mPoint.mAcc);
    121  TextMarkerData markerData(reinterpret_cast<uintptr_t>(doc), mPoint.mAcc->ID(),
    122                            mPoint.mOffset);
    123  AXTextMarkerRef cf_text_marker = AXTextMarkerCreate(
    124      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&markerData),
    125      sizeof(TextMarkerData));
    126 
    127  return (__bridge AXTextMarkerRef)[(__bridge id)(cf_text_marker)autorelease];
    128 }
    129 
    130 bool GeckoTextMarker::Next() {
    131  TextLeafPoint next =
    132      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirNext,
    133                          TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker);
    134 
    135  if (next && next != mPoint) {
    136    mPoint = next;
    137    return true;
    138  }
    139 
    140  return false;
    141 }
    142 
    143 bool GeckoTextMarker::Previous() {
    144  TextLeafPoint prev =
    145      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious,
    146                          TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker);
    147  if (prev && mPoint != prev) {
    148    mPoint = prev;
    149    return true;
    150  }
    151 
    152  return false;
    153 }
    154 
    155 /**
    156 * Return true if the given point is inside editable content.
    157 */
    158 static bool IsPointInEditable(const TextLeafPoint& aPoint) {
    159  if (aPoint.mAcc) {
    160    if (aPoint.mAcc->State() & states::EDITABLE) {
    161      return true;
    162    }
    163 
    164    Accessible* parent = aPoint.mAcc->Parent();
    165    if (parent && (parent->State() & states::EDITABLE)) {
    166      return true;
    167    }
    168  }
    169 
    170  return false;
    171 }
    172 
    173 GeckoTextMarkerRange GeckoTextMarker::LeftWordRange() const {
    174  bool includeCurrentInStart = !mPoint.IsParagraphStart(true);
    175  if (includeCurrentInStart) {
    176    TextLeafPoint prevChar =
    177        mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
    178    if (!prevChar.IsSpace()) {
    179      includeCurrentInStart = false;
    180    }
    181  }
    182 
    183  TextLeafPoint start = mPoint.FindBoundary(
    184      nsIAccessibleText::BOUNDARY_WORD_START, eDirPrevious,
    185      includeCurrentInStart
    186          ? (TextLeafPoint::BoundaryFlags::eIncludeOrigin |
    187             TextLeafPoint::BoundaryFlags::eStopInEditable |
    188             TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker)
    189          : (TextLeafPoint::BoundaryFlags::eStopInEditable |
    190             TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker));
    191 
    192  TextLeafPoint end;
    193  if (start == mPoint) {
    194    end = start.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_END, eDirPrevious,
    195                             TextLeafPoint::BoundaryFlags::eStopInEditable);
    196  }
    197 
    198  if (start != mPoint || end == start) {
    199    end = start.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_END, eDirNext,
    200                             TextLeafPoint::BoundaryFlags::eStopInEditable);
    201    if (end < mPoint && IsPointInEditable(end) && !IsPointInEditable(mPoint)) {
    202      start = end;
    203      end = mPoint;
    204    }
    205  }
    206 
    207  return GeckoTextMarkerRange(start < end ? start : end,
    208                              start < end ? end : start);
    209 }
    210 
    211 GeckoTextMarkerRange GeckoTextMarker::RightWordRange() const {
    212  TextLeafPoint prevChar =
    213      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious,
    214                          TextLeafPoint::BoundaryFlags::eStopInEditable);
    215 
    216  if (prevChar != mPoint && mPoint.IsParagraphStart(true)) {
    217    return GeckoTextMarkerRange(mPoint, mPoint);
    218  }
    219 
    220  TextLeafPoint end =
    221      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_END, eDirNext,
    222                          TextLeafPoint::BoundaryFlags::eStopInEditable);
    223 
    224  if (end == mPoint) {
    225    // No word to the right of this point.
    226    return GeckoTextMarkerRange(mPoint, mPoint);
    227  }
    228 
    229  TextLeafPoint start =
    230      end.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_START, eDirPrevious,
    231                       TextLeafPoint::BoundaryFlags::eStopInEditable);
    232 
    233  if (start.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_END, eDirNext,
    234                         TextLeafPoint::BoundaryFlags::eStopInEditable) <
    235      mPoint) {
    236    // Word end is inside of an input to the left of this.
    237    return GeckoTextMarkerRange(mPoint, mPoint);
    238  }
    239 
    240  if (mPoint < start) {
    241    end = start;
    242    start = mPoint;
    243  }
    244 
    245  return GeckoTextMarkerRange(start < end ? start : end,
    246                              start < end ? end : start);
    247 }
    248 
    249 GeckoTextMarkerRange GeckoTextMarker::LineRange() const {
    250  TextLeafPoint start = mPoint.FindBoundary(
    251      nsIAccessibleText::BOUNDARY_LINE_START, eDirPrevious,
    252      TextLeafPoint::BoundaryFlags::eStopInEditable |
    253          TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker |
    254          TextLeafPoint::BoundaryFlags::eIncludeOrigin);
    255  // If this is a blank line containing only a line feed, the start boundary
    256  // is the same as the end boundary. We do not want to walk to the end of the
    257  // next line.
    258  TextLeafPoint end =
    259      start.IsLineFeedChar()
    260          ? start
    261          : start.FindBoundary(nsIAccessibleText::BOUNDARY_LINE_END, eDirNext,
    262                               TextLeafPoint::BoundaryFlags::eStopInEditable);
    263 
    264  return GeckoTextMarkerRange(start, end);
    265 }
    266 
    267 GeckoTextMarkerRange GeckoTextMarker::LeftLineRange() const {
    268  TextLeafPoint start = mPoint.FindBoundary(
    269      nsIAccessibleText::BOUNDARY_LINE_START, eDirPrevious,
    270      TextLeafPoint::BoundaryFlags::eStopInEditable |
    271          TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker);
    272  TextLeafPoint end =
    273      start.FindBoundary(nsIAccessibleText::BOUNDARY_LINE_END, eDirNext,
    274                         TextLeafPoint::BoundaryFlags::eStopInEditable);
    275 
    276  return GeckoTextMarkerRange(start, end);
    277 }
    278 
    279 GeckoTextMarkerRange GeckoTextMarker::RightLineRange() const {
    280  TextLeafPoint end =
    281      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_LINE_END, eDirNext,
    282                          TextLeafPoint::BoundaryFlags::eStopInEditable);
    283  TextLeafPoint start =
    284      end.FindBoundary(nsIAccessibleText::BOUNDARY_LINE_START, eDirPrevious,
    285                       TextLeafPoint::BoundaryFlags::eStopInEditable);
    286 
    287  return GeckoTextMarkerRange(start, end);
    288 }
    289 
    290 GeckoTextMarkerRange GeckoTextMarker::ParagraphRange() const {
    291  // XXX: WebKit gets trapped in inputs. Maybe we shouldn't?
    292  TextLeafPoint end =
    293      mPoint.FindBoundary(nsIAccessibleText::BOUNDARY_PARAGRAPH, eDirNext,
    294                          TextLeafPoint::BoundaryFlags::eStopInEditable);
    295  TextLeafPoint start =
    296      end.FindBoundary(nsIAccessibleText::BOUNDARY_PARAGRAPH, eDirPrevious,
    297                       TextLeafPoint::BoundaryFlags::eStopInEditable);
    298 
    299  return GeckoTextMarkerRange(start, end);
    300 }
    301 
    302 GeckoTextMarkerRange GeckoTextMarker::StyleRange() const {
    303  if (mPoint.mOffset == 0) {
    304    // If the marker is on the boundary between two leafs, MacOS expects the
    305    // previous leaf.
    306    TextLeafPoint prev = mPoint.FindBoundary(
    307        nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious,
    308        TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker);
    309    if (prev != mPoint) {
    310      return GeckoTextMarker(prev).StyleRange();
    311    }
    312  }
    313 
    314  TextLeafPoint start(mPoint.mAcc, 0);
    315  TextLeafPoint end(mPoint.mAcc, nsAccUtils::TextLength(mPoint.mAcc));
    316  return GeckoTextMarkerRange(start, end);
    317 }
    318 
    319 Accessible* GeckoTextMarker::Leaf() {
    320  MOZ_ASSERT(mPoint.mAcc);
    321  Accessible* acc = mPoint.mAcc;
    322  if (mPoint.mOffset == 0) {
    323    // If the marker is on the boundary between two leafs, MacOS expects the
    324    // previous leaf.
    325    TextLeafPoint prev = mPoint.FindBoundary(
    326        nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious,
    327        TextLeafPoint::BoundaryFlags::eIgnoreListItemMarker);
    328    acc = prev.mAcc;
    329  }
    330 
    331  Accessible* parent = acc->Parent();
    332  return parent && nsAccUtils::MustPrune(parent) ? parent : acc;
    333 }
    334 
    335 // GeckoTextMarkerRange
    336 
    337 GeckoTextMarkerRange::GeckoTextMarkerRange(Accessible* aAccessible) {
    338  mRange = TextLeafRange(
    339      TextLeafPoint(aAccessible, 0),
    340      TextLeafPoint(aAccessible, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT));
    341 }
    342 
    343 GeckoTextMarkerRange GeckoTextMarkerRange::MarkerRangeFromAXTextMarkerRange(
    344    Accessible* aDoc, AXTextMarkerRangeRef aTextMarkerRange) {
    345  if (!aTextMarkerRange ||
    346      CFGetTypeID(aTextMarkerRange) != AXTextMarkerRangeGetTypeID()) {
    347    return GeckoTextMarkerRange();
    348  }
    349 
    350  AXTextMarkerRef start_marker(
    351      AXTextMarkerRangeCopyStartMarker(aTextMarkerRange));
    352  AXTextMarkerRef end_marker(AXTextMarkerRangeCopyEndMarker(aTextMarkerRange));
    353 
    354  GeckoTextMarker start =
    355      GeckoTextMarker::MarkerFromAXTextMarker(aDoc, start_marker);
    356  GeckoTextMarker end =
    357      GeckoTextMarker::MarkerFromAXTextMarker(aDoc, end_marker);
    358 
    359  CFRelease(start_marker);
    360  CFRelease(end_marker);
    361 
    362  return GeckoTextMarkerRange(start, end);
    363 }
    364 
    365 AXTextMarkerRangeRef GeckoTextMarkerRange::CreateAXTextMarkerRange() {
    366  if (!IsValid()) {
    367    return nil;
    368  }
    369 
    370  GeckoTextMarker start = GeckoTextMarker(mRange.Start());
    371  GeckoTextMarker end = GeckoTextMarker(mRange.End());
    372 
    373  AXTextMarkerRangeRef cf_text_marker_range =
    374      AXTextMarkerRangeCreate(kCFAllocatorDefault, start.CreateAXTextMarker(),
    375                              end.CreateAXTextMarker());
    376 
    377  return (__bridge AXTextMarkerRangeRef)[(__bridge id)(
    378      cf_text_marker_range)autorelease];
    379 }
    380 
    381 NSString* GeckoTextMarkerRange::Text() const {
    382  if (mRange.Start() == mRange.End()) {
    383    return @"";
    384  }
    385 
    386  if ((mRange.Start().mAcc == mRange.End().mAcc) &&
    387      (mRange.Start().mAcc->ChildCount() == 0) &&
    388      (mRange.Start().mAcc->State() & states::EDITABLE)) {
    389    return @"";
    390  }
    391 
    392  nsAutoString text;
    393  TextLeafPoint prev = mRange.Start().FindBoundary(
    394      nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
    395  TextLeafRange range =
    396      prev != mRange.Start() && prev.mAcc->Role() == roles::LISTITEM_MARKER
    397          ? TextLeafRange(TextLeafPoint(prev.mAcc, 0), mRange.End())
    398          : mRange;
    399 
    400  for (TextLeafRange segment : range) {
    401    TextLeafPoint start = segment.Start();
    402    if (start.mAcc->IsMenuPopup() &&
    403        (start.mAcc->State() & states::INVISIBLE)) {
    404      // XXX: Invisible XUL menu popups are in our tree and we need to skip
    405      // them.
    406      continue;
    407    }
    408    if (start.mAcc->IsTextField() && start.mAcc->ChildCount() == 0) {
    409      continue;
    410    }
    411 
    412    start.mAcc->AppendTextTo(text, start.mOffset,
    413                             segment.End().mOffset - start.mOffset);
    414  }
    415 
    416  return nsCocoaUtils::ToNSString(text);
    417 }
    418 
    419 static void AppendTextToAttributedString(
    420    NSMutableAttributedString* aAttributedString, Accessible* aAccessible,
    421    const nsString& aString, AccAttributes* aAttributes) {
    422  NSAttributedString* substr = [[[NSAttributedString alloc]
    423      initWithString:nsCocoaUtils::ToNSString(aString)
    424          attributes:utils::StringAttributesFromAccAttributes(
    425                         aAttributes, aAccessible)] autorelease];
    426 
    427  [aAttributedString appendAttributedString:substr];
    428 }
    429 
    430 static RefPtr<AccAttributes> GetTextAttributes(TextLeafPoint aPoint) {
    431  RefPtr<AccAttributes> attrs = aPoint.GetTextAttributes();
    432  if (!attrs) {
    433    // If we can't fetch text attributes for the given point, return null.
    434    // We avoid creating a new AccAttributes here because our AttributedText()
    435    // code below relies on this null return value to indicate we're dealing
    436    // with a non-text control.
    437    return nullptr;
    438  }
    439  // Mac expects some object properties to be exposed as text attributes. We
    440  // add these here rather than in utils::StringAttributesFromAccAttributes so
    441  // we can use AccAttributes::Equal to determine whether we need to start a new
    442  // run, rather than needing additional special case comparisons.
    443  for (Accessible* ancestor = aPoint.mAcc->Parent();
    444       ancestor && !ancestor->IsDoc(); ancestor = ancestor->Parent()) {
    445    if (ancestor->Role() == roles::MARK) {
    446      attrs->SetAttribute(nsGkAtoms::mark, true);
    447    }
    448  }
    449  return attrs;
    450 }
    451 
    452 NSAttributedString* GeckoTextMarkerRange::AttributedText() const {
    453  NSMutableAttributedString* str =
    454      [[[NSMutableAttributedString alloc] init] autorelease];
    455 
    456  if (mRange.Start() == mRange.End()) {
    457    return str;
    458  }
    459 
    460  if ((mRange.Start().mAcc == mRange.End().mAcc) &&
    461      (mRange.Start().mAcc->ChildCount() == 0) &&
    462      (mRange.Start().mAcc->IsTextField())) {
    463    return str;
    464  }
    465 
    466  TextLeafPoint prev = mRange.Start().FindBoundary(
    467      nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
    468  TextLeafRange range =
    469      prev != mRange.Start() && prev.mAcc->Role() == roles::LISTITEM_MARKER
    470          ? TextLeafRange(TextLeafPoint(prev.mAcc, 0), mRange.End())
    471          : mRange;
    472 
    473  nsAutoString text;
    474  TextLeafPoint start = range.Start();
    475  const TextLeafPoint stop = range.End();
    476  RefPtr<AccAttributes> currentRun = GetTextAttributes(start);
    477  Accessible* runAcc = start.mAcc;
    478  do {
    479    TextLeafPoint attributesNext = start.FindTextAttrsStart(eDirNext, false);
    480    if (stop < attributesNext) {
    481      attributesNext = stop;
    482    }
    483    if (start.mAcc->IsMenuPopup() &&
    484        (start.mAcc->State() & states::INVISIBLE)) {
    485      // XXX: Invisible XUL menu popups are in our tree and we need to skip
    486      // them.
    487      start = attributesNext;
    488      continue;
    489    }
    490    RefPtr<AccAttributes> attributes = GetTextAttributes(start);
    491    if (!currentRun || !attributes || !attributes->Equal(currentRun)) {
    492      // If currentRun is null this is a non-text control and we will
    493      // append a run with no text or attributes, just an AXAttachment
    494      // referencing this accessible.
    495      AppendTextToAttributedString(str, runAcc, text, currentRun);
    496      text.Truncate();
    497      currentRun = attributes;
    498      runAcc = start.mAcc;
    499    }
    500    for (TextLeafRange segment : TextLeafRange(start, attributesNext)) {
    501      TextLeafPoint segStart = segment.Start();
    502      segStart.mAcc->AppendTextTo(text, segStart.mOffset,
    503                                  segment.End().mOffset - segStart.mOffset);
    504    }
    505    start = attributesNext;
    506  } while (start != stop);
    507 
    508  if (!text.IsEmpty()) {
    509    AppendTextToAttributedString(str, runAcc, text, currentRun);
    510  }
    511 
    512  return str;
    513 }
    514 
    515 int32_t GeckoTextMarkerRange::Length() const {
    516  int32_t length = 0;
    517  for (TextLeafRange segment : mRange) {
    518    if (segment.End().mAcc->Role() == roles::LISTITEM_MARKER) {
    519      // XXX: MacOS expects bullets to be in the range's text, but not in
    520      // the calculated length!
    521      continue;
    522    }
    523    length += segment.End().mOffset - segment.Start().mOffset;
    524  }
    525 
    526  return length;
    527 }
    528 
    529 NSValue* GeckoTextMarkerRange::Bounds() const {
    530  LayoutDeviceIntRect rect = mRange ? mRange.Bounds() : LayoutDeviceIntRect();
    531  // We need to find the NSScreen that this range belongs to. Because we
    532  // are not guaranteed to get a native accessible for the range's start or
    533  // end acc (whitespace, for ex. has no native acc) we fetch the doc
    534  // and then use its native acc to retrieve the screen.
    535  Accessible* acc = nsAccUtils::DocumentFor(mRange.Start().mAcc);
    536  NSScreen* screen =
    537      utils::GetNSScreenForAcc(GetNativeFromGeckoAccessible(acc));
    538  CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(screen);
    539 
    540  // Regardless of screen selected above, VO is only happy if we use the
    541  // main screen height for Y coordinate conversion. This is consistent with
    542  // moxHitTest and moxFrame.
    543  NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
    544  NSRect r =
    545      NSMakeRect(static_cast<CGFloat>(rect.x) / scaleFactor,
    546                 [mainView frame].size.height -
    547                     static_cast<CGFloat>(rect.y + rect.height) / scaleFactor,
    548                 static_cast<CGFloat>(rect.width) / scaleFactor,
    549                 static_cast<CGFloat>(rect.height) / scaleFactor);
    550 
    551  return [NSValue valueWithRect:r];
    552 }
    553 
    554 void GeckoTextMarkerRange::Select() const { mRange.SetSelection(0); }
    555 
    556 }  // namespace a11y
    557 }  // namespace mozilla