Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / accessible / mac / GeckoTextMarker.mm
blobd7233964f0dcfdb5d9d2b01195faf6cf4f25f0f6
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/. */
8 #import "GeckoTextMarker.h"
10 #include "DocAccessible.h"
11 #include "DocAccessibleParent.h"
12 #include "AccAttributes.h"
13 #include "nsCocoaUtils.h"
14 #include "MOXAccessibleBase.h"
15 #include "mozAccessible.h"
17 #include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
19 namespace mozilla {
20 namespace a11y {
22 struct OpaqueGeckoTextMarker {
23   OpaqueGeckoTextMarker(uintptr_t aDoc, uintptr_t aID, int32_t aOffset)
24       : mDoc(aDoc), mID(aID), mOffset(aOffset) {}
25   OpaqueGeckoTextMarker() {}
26   uintptr_t mDoc;
27   uintptr_t mID;
28   int32_t mOffset;
31 static bool DocumentExists(Accessible* aDoc, uintptr_t aDocPtr) {
32   if (reinterpret_cast<uintptr_t>(aDoc) == aDocPtr) {
33     return true;
34   }
36   if (aDoc->IsLocal()) {
37     DocAccessible* docAcc = aDoc->AsLocal()->AsDoc();
38     uint32_t docCount = docAcc->ChildDocumentCount();
39     for (uint32_t i = 0; i < docCount; i++) {
40       if (DocumentExists(docAcc->GetChildDocumentAt(i), aDocPtr)) {
41         return true;
42       }
43     }
44   } else {
45     DocAccessibleParent* docProxy = aDoc->AsRemote()->AsDoc();
46     size_t docCount = docProxy->ChildDocCount();
47     for (uint32_t i = 0; i < docCount; i++) {
48       if (DocumentExists(docProxy->ChildDocAt(i), aDocPtr)) {
49         return true;
50       }
51     }
52   }
54   return false;
57 // GeckoTextMarker
59 GeckoTextMarker::GeckoTextMarker(Accessible* aDoc,
60                                  AXTextMarkerRef aTextMarker) {
61   MOZ_ASSERT(aDoc);
62   OpaqueGeckoTextMarker opaqueMarker;
63   if (aTextMarker &&
64       AXTextMarkerGetLength(aTextMarker) == sizeof(OpaqueGeckoTextMarker)) {
65     memcpy(&opaqueMarker, AXTextMarkerGetBytePtr(aTextMarker),
66            sizeof(OpaqueGeckoTextMarker));
67     if (DocumentExists(aDoc, opaqueMarker.mDoc)) {
68       Accessible* doc = reinterpret_cast<Accessible*>(opaqueMarker.mDoc);
69       if (doc->IsRemote()) {
70         mContainer = doc->AsRemote()->AsDoc()->GetAccessible(opaqueMarker.mID);
71       } else {
72         mContainer = doc->AsLocal()->AsDoc()->GetAccessibleByUniqueID(
73             reinterpret_cast<void*>(opaqueMarker.mID));
74       }
75     }
77     mOffset = opaqueMarker.mOffset;
78   } else {
79     mContainer = nullptr;
80     mOffset = 0;
81   }
84 GeckoTextMarker GeckoTextMarker::MarkerFromIndex(Accessible* aRoot,
85                                                  int32_t aIndex) {
86   if (aRoot->IsRemote()) {
87     int32_t offset = 0;
88     uint64_t containerID = 0;
89     DocAccessibleParent* ipcDoc = aRoot->AsRemote()->Document();
90     Unused << ipcDoc->GetPlatformExtension()->SendOffsetAtIndex(
91         aRoot->AsRemote()->ID(), aIndex, &containerID, &offset);
92     RemoteAccessible* container = ipcDoc->GetAccessible(containerID);
93     return GeckoTextMarker(container, offset);
94   } else if (auto htWrap = static_cast<HyperTextAccessibleWrap*>(
95                  aRoot->AsLocal()->AsHyperText())) {
96     int32_t offset = 0;
97     HyperTextAccessible* container = nullptr;
98     htWrap->OffsetAtIndex(aIndex, &container, &offset);
99     return GeckoTextMarker(container, offset);
100   }
102   return GeckoTextMarker();
105 AXTextMarkerRef GeckoTextMarker::CreateAXTextMarker() {
106   if (!IsValid()) {
107     return nil;
108   }
110   Accessible* doc;
111   if (mContainer->IsRemote()) {
112     doc = mContainer->AsRemote()->Document();
113   } else {
114     doc = mContainer->AsLocal()->Document();
115   }
117   uintptr_t identifier =
118       mContainer->IsRemote()
119           ? mContainer->AsRemote()->ID()
120           : reinterpret_cast<uintptr_t>(mContainer->AsLocal()->UniqueID());
122   OpaqueGeckoTextMarker opaqueMarker(reinterpret_cast<uintptr_t>(doc),
123                                      identifier, mOffset);
124   AXTextMarkerRef cf_text_marker = AXTextMarkerCreate(
125       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&opaqueMarker),
126       sizeof(OpaqueGeckoTextMarker));
128   return (__bridge AXTextMarkerRef)[(__bridge id)(cf_text_marker)autorelease];
131 bool GeckoTextMarker::operator<(const GeckoTextMarker& aPoint) const {
132   if (mContainer == aPoint.mContainer) return mOffset < aPoint.mOffset;
134   // Build the chain of parents
135   AutoTArray<Accessible*, 30> parents1, parents2;
136   Accessible* p1 = mContainer;
137   while (p1) {
138     parents1.AppendElement(p1);
139     p1 = p1->Parent();
140   }
142   Accessible* p2 = aPoint.mContainer;
143   while (p2) {
144     parents2.AppendElement(p2);
145     p2 = p2->Parent();
146   }
148   // An empty chain of parents means one of the containers was null.
149   MOZ_ASSERT(parents1.Length() != 0 && parents2.Length() != 0,
150              "have empty chain of parents!");
152   // Find where the parent chain differs
153   uint32_t pos1 = parents1.Length(), pos2 = parents2.Length();
154   for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
155     Accessible* child1 = parents1.ElementAt(--pos1);
156     Accessible* child2 = parents2.ElementAt(--pos2);
157     if (child1 != child2) {
158       return child1->IndexInParent() < child2->IndexInParent();
159     }
160   }
162   if (pos1 != 0) {
163     // If parents1 is a superset of parents2 then mContainer is a
164     // descendant of aPoint.mContainer. The next element down in parents1
165     // is mContainer's ancestor that is the child of aPoint.mContainer.
166     // We compare its end offset in aPoint.mContainer with aPoint.mOffset.
167     Accessible* child = parents1.ElementAt(pos1 - 1);
168     MOZ_ASSERT(child->Parent() == aPoint.mContainer);
169     uint32_t endOffset = child->EndOffset();
170     return endOffset < static_cast<uint32_t>(aPoint.mOffset);
171   }
173   if (pos2 != 0) {
174     // If parents2 is a superset of parents1 then aPoint.mContainer is a
175     // descendant of mContainer. The next element down in parents2
176     // is aPoint.mContainer's ancestor that is the child of mContainer.
177     // We compare its start offset in mContainer with mOffset.
178     Accessible* child = parents2.ElementAt(pos2 - 1);
179     MOZ_ASSERT(child->Parent() == mContainer);
180     uint32_t startOffset = child->StartOffset();
181     return static_cast<uint32_t>(mOffset) <= startOffset;
182   }
184   MOZ_ASSERT_UNREACHABLE("Broken tree?!");
185   return false;
188 bool GeckoTextMarker::IsEditableRoot() {
189   uint64_t state = mContainer->IsRemote() ? mContainer->AsRemote()->State()
190                                           : mContainer->AsLocal()->State();
191   if ((state & states::EDITABLE) == 0) {
192     return false;
193   }
195   Accessible* parent = mContainer->Parent();
196   if (!parent) {
197     // Not sure when this can happen, but it would technically be an editable
198     // root.
199     return true;
200   }
202   state = parent->IsRemote() ? parent->AsRemote()->State()
203                              : parent->AsLocal()->State();
205   return (state & states::EDITABLE) == 0;
208 bool GeckoTextMarker::Next() {
209   if (mContainer->IsRemote()) {
210     int32_t nextOffset = 0;
211     uint64_t nextContainerID = 0;
212     DocAccessibleParent* ipcDoc = mContainer->AsRemote()->Document();
213     Unused << ipcDoc->GetPlatformExtension()->SendNextClusterAt(
214         mContainer->AsRemote()->ID(), mOffset, &nextContainerID, &nextOffset);
215     RemoteAccessible* nextContainer = ipcDoc->GetAccessible(nextContainerID);
216     bool moved =
217         nextContainer != mContainer->AsRemote() || nextOffset != mOffset;
218     mContainer = nextContainer;
219     mOffset = nextOffset;
220     return moved;
221   } else if (auto htWrap = ContainerAsHyperTextWrap()) {
222     HyperTextAccessible* nextContainer = nullptr;
223     int32_t nextOffset = 0;
224     htWrap->NextClusterAt(mOffset, &nextContainer, &nextOffset);
225     bool moved = nextContainer != htWrap || nextOffset != mOffset;
226     mContainer = nextContainer;
227     mOffset = nextOffset;
228     return moved;
229   }
231   return false;
234 bool GeckoTextMarker::Previous() {
235   if (mContainer->IsRemote()) {
236     int32_t prevOffset = 0;
237     uint64_t prevContainerID = 0;
238     DocAccessibleParent* ipcDoc = mContainer->AsRemote()->Document();
239     Unused << ipcDoc->GetPlatformExtension()->SendPreviousClusterAt(
240         mContainer->AsRemote()->ID(), mOffset, &prevContainerID, &prevOffset);
241     RemoteAccessible* prevContainer = ipcDoc->GetAccessible(prevContainerID);
242     bool moved =
243         prevContainer != mContainer->AsRemote() || prevOffset != mOffset;
244     mContainer = prevContainer;
245     mOffset = prevOffset;
246     return moved;
247   } else if (auto htWrap = ContainerAsHyperTextWrap()) {
248     HyperTextAccessible* prevContainer = nullptr;
249     int32_t prevOffset = 0;
250     htWrap->PreviousClusterAt(mOffset, &prevContainer, &prevOffset);
251     bool moved = prevContainer != htWrap || prevOffset != mOffset;
252     mContainer = prevContainer;
253     mOffset = prevOffset;
254     return moved;
255   }
257   return false;
260 static uint32_t CharacterCount(Accessible* aContainer) {
261   if (aContainer->IsRemote()) {
262     return aContainer->AsRemote()->CharacterCount();
263   }
265   if (aContainer->AsLocal()->IsHyperText()) {
266     return aContainer->AsLocal()->AsHyperText()->CharacterCount();
267   }
269   return 0;
272 GeckoTextMarkerRange GeckoTextMarker::Range(EWhichRange aRangeType) {
273   MOZ_ASSERT(mContainer);
274   if (mContainer->IsRemote()) {
275     int32_t startOffset = 0, endOffset = 0;
276     uint64_t startContainerID = 0, endContainerID = 0;
277     DocAccessibleParent* ipcDoc = mContainer->AsRemote()->Document();
278     bool success = ipcDoc->GetPlatformExtension()->SendRangeAt(
279         mContainer->AsRemote()->ID(), mOffset, aRangeType, &startContainerID,
280         &startOffset, &endContainerID, &endOffset);
281     if (success) {
282       return GeckoTextMarkerRange(
283           GeckoTextMarker(ipcDoc->GetAccessible(startContainerID), startOffset),
284           GeckoTextMarker(ipcDoc->GetAccessible(endContainerID), endOffset));
285     }
286   } else if (auto htWrap = ContainerAsHyperTextWrap()) {
287     int32_t startOffset = 0, endOffset = 0;
288     HyperTextAccessible* startContainer = nullptr;
289     HyperTextAccessible* endContainer = nullptr;
290     htWrap->RangeAt(mOffset, aRangeType, &startContainer, &startOffset,
291                     &endContainer, &endOffset);
292     return GeckoTextMarkerRange(GeckoTextMarker(startContainer, startOffset),
293                                 GeckoTextMarker(endContainer, endOffset));
294   }
296   return GeckoTextMarkerRange(GeckoTextMarker(), GeckoTextMarker());
299 Accessible* GeckoTextMarker::Leaf() {
300   MOZ_ASSERT(mContainer);
301   if (mContainer->IsRemote()) {
302     uint64_t leafID = 0;
303     DocAccessibleParent* ipcDoc = mContainer->AsRemote()->Document();
304     Unused << ipcDoc->GetPlatformExtension()->SendLeafAtOffset(
305         mContainer->AsRemote()->ID(), mOffset, &leafID);
306     return ipcDoc->GetAccessible(leafID);
307   } else if (auto htWrap = ContainerAsHyperTextWrap()) {
308     return htWrap->LeafAtOffset(mOffset);
309   }
311   return mContainer;
314 // GeckoTextMarkerRange
316 GeckoTextMarkerRange::GeckoTextMarkerRange(
317     Accessible* aDoc, AXTextMarkerRangeRef aTextMarkerRange) {
318   if (!aTextMarkerRange ||
319       CFGetTypeID(aTextMarkerRange) != AXTextMarkerRangeGetTypeID()) {
320     return;
321   }
323   AXTextMarkerRef start_marker(
324       AXTextMarkerRangeCopyStartMarker(aTextMarkerRange));
325   AXTextMarkerRef end_marker(AXTextMarkerRangeCopyEndMarker(aTextMarkerRange));
327   mStart = GeckoTextMarker(aDoc, start_marker);
328   mEnd = GeckoTextMarker(aDoc, end_marker);
330   CFRelease(start_marker);
331   CFRelease(end_marker);
334 GeckoTextMarkerRange::GeckoTextMarkerRange(Accessible* aAccessible) {
335   if (aAccessible->IsHyperText()) {
336     // The accessible is a hypertext. Initialize range to its inner text range.
337     mStart = GeckoTextMarker(aAccessible, 0);
338     mEnd = GeckoTextMarker(aAccessible, (CharacterCount(aAccessible)));
339   } else {
340     // The accessible is not a hypertext (maybe a text leaf?). Initialize range
341     // to its offsets in its container.
342     mStart = GeckoTextMarker(aAccessible->Parent(), 0);
343     mEnd = GeckoTextMarker(aAccessible->Parent(), 0);
344     if (mStart.mContainer->IsRemote()) {
345       DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
346       Unused << ipcDoc->GetPlatformExtension()->SendRangeOfChild(
347           mStart.mContainer->AsRemote()->ID(), aAccessible->AsRemote()->ID(),
348           &mStart.mOffset, &mEnd.mOffset);
349     } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) {
350       htWrap->RangeOfChild(aAccessible->AsLocal(), &mStart.mOffset,
351                            &mEnd.mOffset);
352     }
353   }
356 AXTextMarkerRangeRef GeckoTextMarkerRange::CreateAXTextMarkerRange() {
357   if (!IsValid()) {
358     return nil;
359   }
361   AXTextMarkerRangeRef cf_text_marker_range =
362       AXTextMarkerRangeCreate(kCFAllocatorDefault, mStart.CreateAXTextMarker(),
363                               mEnd.CreateAXTextMarker());
365   return (__bridge AXTextMarkerRangeRef)[(__bridge id)(
366       cf_text_marker_range)autorelease];
369 NSString* GeckoTextMarkerRange::Text() const {
370   nsAutoString text;
371   if (mStart.mContainer->IsRemote() && mEnd.mContainer->IsRemote()) {
372     DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
373     Unused << ipcDoc->GetPlatformExtension()->SendTextForRange(
374         mStart.mContainer->AsRemote()->ID(), mStart.mOffset,
375         mEnd.mContainer->AsRemote()->ID(), mEnd.mOffset, &text);
376   } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) {
377     htWrap->TextForRange(text, mStart.mOffset, mEnd.ContainerAsHyperTextWrap(),
378                          mEnd.mOffset);
379   }
380   return nsCocoaUtils::ToNSString(text);
383 static NSColor* ColorFromColor(const Color& aColor) {
384   return [NSColor colorWithCalibratedRed:NS_GET_R(aColor.mValue) / 255.0
385                                    green:NS_GET_G(aColor.mValue) / 255.0
386                                     blue:NS_GET_B(aColor.mValue) / 255.0
387                                    alpha:1.0];
390 static NSDictionary* StringAttributesFromAttributes(AccAttributes* aAttributes,
391                                                     Accessible* aContainer) {
392   NSMutableDictionary* attrDict =
393       [NSMutableDictionary dictionaryWithCapacity:aAttributes->Count()];
394   NSMutableDictionary* fontAttrDict = [[NSMutableDictionary alloc] init];
395   [attrDict setObject:fontAttrDict forKey:@"AXFont"];
396   for (auto iter : *aAttributes) {
397     if (iter.Name() == nsGkAtoms::backgroundColor) {
398       if (Maybe<Color> value = iter.Value<Color>()) {
399         NSColor* color = ColorFromColor(*value);
400         [attrDict setObject:(__bridge id)color.CGColor
401                      forKey:@"AXBackgroundColor"];
402       }
403     } else if (iter.Name() == nsGkAtoms::color) {
404       if (Maybe<Color> value = iter.Value<Color>()) {
405         NSColor* color = ColorFromColor(*value);
406         [attrDict setObject:(__bridge id)color.CGColor
407                      forKey:@"AXForegroundColor"];
408       }
409     } else if (iter.Name() == nsGkAtoms::font_size) {
410       if (Maybe<FontSize> pointSize = iter.Value<FontSize>()) {
411         int32_t fontPixelSize = static_cast<int32_t>(pointSize->mValue * 4 / 3);
412         [fontAttrDict setObject:@(fontPixelSize) forKey:@"AXFontSize"];
413       }
414     } else if (iter.Name() == nsGkAtoms::font_family) {
415       nsAutoString fontFamily;
416       iter.ValueAsString(fontFamily);
417       [fontAttrDict setObject:nsCocoaUtils::ToNSString(fontFamily)
418                        forKey:@"AXFontFamily"];
419     } else if (iter.Name() == nsGkAtoms::textUnderlineColor) {
420       [attrDict setObject:@1 forKey:@"AXUnderline"];
421       if (Maybe<Color> value = iter.Value<Color>()) {
422         NSColor* color = ColorFromColor(*value);
423         [attrDict setObject:(__bridge id)color.CGColor
424                      forKey:@"AXUnderlineColor"];
425       }
426     } else if (iter.Name() == nsGkAtoms::invalid) {
427       // XXX: There is currently no attribute for grammar
428       if (auto value = iter.Value<RefPtr<nsAtom>>()) {
429         if (*value == nsGkAtoms::spelling) {
430           [attrDict setObject:@YES
431                        forKey:NSAccessibilityMarkedMisspelledTextAttribute];
432         }
433       }
434     } else {
435       nsAutoString valueStr;
436       iter.ValueAsString(valueStr);
437       nsAutoString keyStr;
438       iter.NameAsString(keyStr);
439       [attrDict setObject:nsCocoaUtils::ToNSString(valueStr)
440                    forKey:nsCocoaUtils::ToNSString(keyStr)];
441     }
442   }
444   mozAccessible* container = GetNativeFromGeckoAccessible(aContainer);
445   id<MOXAccessible> link =
446       [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) {
447         return [[moxAcc moxRole] isEqualToString:NSAccessibilityLinkRole];
448       }];
449   if (link) {
450     [attrDict setObject:link forKey:@"AXLink"];
451   }
453   id<MOXAccessible> heading =
454       [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) {
455         return [[moxAcc moxRole] isEqualToString:@"AXHeading"];
456       }];
457   if (heading) {
458     [attrDict setObject:[heading moxValue] forKey:@"AXHeadingLevel"];
459   }
461   return attrDict;
464 NSAttributedString* GeckoTextMarkerRange::AttributedText() const {
465   NSMutableAttributedString* str =
466       [[[NSMutableAttributedString alloc] init] autorelease];
468   if (mStart.mContainer->IsRemote() && mEnd.mContainer->IsRemote()) {
469     nsTArray<TextAttributesRun> textAttributesRuns;
470     DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
471     Unused << ipcDoc->GetPlatformExtension()->SendAttributedTextForRange(
472         mStart.mContainer->AsRemote()->ID(), mStart.mOffset,
473         mEnd.mContainer->AsRemote()->ID(), mEnd.mOffset, &textAttributesRuns);
475     for (size_t i = 0; i < textAttributesRuns.Length(); i++) {
476       AccAttributes* attributes =
477           textAttributesRuns.ElementAt(i).TextAttributes();
478       RemoteAccessible* container =
479           ipcDoc->GetAccessible(textAttributesRuns.ElementAt(i).ContainerID());
481       NSAttributedString* substr = [[[NSAttributedString alloc]
482           initWithString:nsCocoaUtils::ToNSString(
483                              textAttributesRuns.ElementAt(i).Text())
484               attributes:StringAttributesFromAttributes(attributes, container)]
485           autorelease];
487       [str appendAttributedString:substr];
488     }
489   } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) {
490     nsTArray<nsString> texts;
491     nsTArray<LocalAccessible*> containers;
492     nsTArray<RefPtr<AccAttributes>> props;
494     htWrap->AttributedTextForRange(texts, props, containers, mStart.mOffset,
495                                    mEnd.ContainerAsHyperTextWrap(),
496                                    mEnd.mOffset);
498     MOZ_ASSERT(texts.Length() == props.Length() &&
499                texts.Length() == containers.Length());
501     for (size_t i = 0; i < texts.Length(); i++) {
502       NSAttributedString* substr = [[[NSAttributedString alloc]
503           initWithString:nsCocoaUtils::ToNSString(texts.ElementAt(i))
504               attributes:StringAttributesFromAttributes(
505                              props.ElementAt(i), containers.ElementAt(i))]
506           autorelease];
507       [str appendAttributedString:substr];
508     }
509   }
511   return str;
514 int32_t GeckoTextMarkerRange::Length() const {
515   int32_t length = 0;
516   if (mStart.mContainer->IsRemote() && mEnd.mContainer->IsRemote()) {
517     DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
518     Unused << ipcDoc->GetPlatformExtension()->SendLengthForRange(
519         mStart.mContainer->AsRemote()->ID(), mStart.mOffset,
520         mEnd.mContainer->AsRemote()->ID(), mEnd.mOffset, &length);
521   } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) {
522     length = htWrap->LengthForRange(
523         mStart.mOffset, mEnd.ContainerAsHyperTextWrap(), mEnd.mOffset);
524   }
526   return length;
529 NSValue* GeckoTextMarkerRange::Bounds() const {
530   LayoutDeviceIntRect rect;
531   if (mStart.mContainer->IsRemote() && mEnd.mContainer->IsRemote()) {
532     DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
533     Unused << ipcDoc->GetPlatformExtension()->SendBoundsForRange(
534         mStart.mContainer->AsRemote()->ID(), mStart.mOffset,
535         mEnd.mContainer->AsRemote()->ID(), mEnd.mOffset, &rect);
536   } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) {
537     rect = htWrap->BoundsForRange(
538         mStart.mOffset, mEnd.ContainerAsHyperTextWrap(), mEnd.mOffset);
539   }
541   NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
542   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
543   NSRect r =
544       NSMakeRect(static_cast<CGFloat>(rect.x) / scaleFactor,
545                  [mainView frame].size.height -
546                      static_cast<CGFloat>(rect.y + rect.height) / scaleFactor,
547                  static_cast<CGFloat>(rect.width) / scaleFactor,
548                  static_cast<CGFloat>(rect.height) / scaleFactor);
550   return [NSValue valueWithRect:r];
553 void GeckoTextMarkerRange::Select() const {
554   if (mStart.mContainer->IsRemote() && mEnd.mContainer->IsRemote()) {
555     DocAccessibleParent* ipcDoc = mStart.mContainer->AsRemote()->Document();
556     Unused << ipcDoc->GetPlatformExtension()->SendSelectRange(
557         mStart.mContainer->AsRemote()->ID(), mStart.mOffset,
558         mEnd.mContainer->AsRemote()->ID(), mEnd.mOffset);
559   } else if (RefPtr<HyperTextAccessibleWrap> htWrap =
560                  mStart.ContainerAsHyperTextWrap()) {
561     RefPtr<HyperTextAccessibleWrap> end = mEnd.ContainerAsHyperTextWrap();
562     htWrap->SelectRange(mStart.mOffset, end, mEnd.mOffset);
563   }
566 bool GeckoTextMarkerRange::Crop(Accessible* aContainer) {
567   GeckoTextMarker containerStart(aContainer, 0);
568   GeckoTextMarker containerEnd(aContainer, CharacterCount(aContainer));
570   if (mEnd < containerStart || containerEnd < mStart) {
571     // The range ends before the container, or starts after it.
572     return false;
573   }
575   if (mStart < containerStart) {
576     // If range start is before container start, adjust range start to
577     // start of container.
578     mStart = containerStart;
579   }
581   if (containerEnd < mEnd) {
582     // If range end is after container end, adjust range end to end of
583     // container.
584     mEnd = containerEnd;
585   }
587   return true;