tor-browser

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

mozTextAccessible.mm (10554B)


      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 #include "AccAttributes.h"
      9 #include "HyperTextAccessible-inl.h"
     10 #include "LocalAccessible-inl.h"
     11 #include "mozilla/a11y/PDocAccessible.h"
     12 #include "nsCocoaUtils.h"
     13 #include "nsObjCExceptions.h"
     14 #include "TextLeafAccessible.h"
     15 
     16 #import "mozTextAccessible.h"
     17 #import "GeckoTextMarker.h"
     18 #import "MOXTextMarkerDelegate.h"
     19 
     20 using namespace mozilla;
     21 using namespace mozilla::a11y;
     22 
     23 inline bool ToNSRange(id aValue, NSRange* aRange) {
     24  MOZ_ASSERT(aRange, "aRange is nil");
     25 
     26  if ([aValue isKindOfClass:[NSValue class]] &&
     27      strcmp([(NSValue*)aValue objCType], @encode(NSRange)) == 0) {
     28    *aRange = [aValue rangeValue];
     29    return true;
     30  }
     31 
     32  return false;
     33 }
     34 
     35 inline NSString* ToNSString(id aValue) {
     36  if ([aValue isKindOfClass:[NSString class]]) {
     37    return aValue;
     38  }
     39 
     40  return nil;
     41 }
     42 
     43 static GeckoTextMarkerRange GetSelectionInObject(mozAccessible* aObj) {
     44  id<MOXTextMarkerSupport> delegate = [aObj moxTextMarkerDelegate];
     45  GeckoTextMarkerRange selection =
     46      [static_cast<MOXTextMarkerDelegate*>(delegate) selection];
     47 
     48  if (!selection.IsValid() || !selection.Crop([aObj geckoAccessible])) {
     49    // The selection is not in this accessible. Return invalid range.
     50    return GeckoTextMarkerRange();
     51  }
     52 
     53  return selection;
     54 }
     55 
     56 static GeckoTextMarkerRange GetTextMarkerRangeFromRange(mozAccessible* aObj,
     57                                                        NSValue* aRange) {
     58  NSRange r = [aRange rangeValue];
     59  Accessible* acc = [aObj geckoAccessible];
     60 
     61  GeckoTextMarker startMarker =
     62      GeckoTextMarker::MarkerFromIndex(acc, r.location);
     63 
     64  GeckoTextMarker endMarker =
     65      GeckoTextMarker::MarkerFromIndex(acc, r.location + r.length);
     66 
     67  return GeckoTextMarkerRange(startMarker, endMarker);
     68 }
     69 
     70 @implementation mozAccessible (TextField)
     71 
     72 - (NSNumber*)moxInsertionPointLineNumber {
     73  MOZ_ASSERT(mGeckoAccessible);
     74 
     75  int32_t lineNumber = -1;
     76  if (HyperTextAccessibleBase* textAcc = mGeckoAccessible->AsHyperTextBase()) {
     77    lineNumber = textAcc->CaretLineNumber() - 1;
     78  }
     79 
     80  return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil;
     81 }
     82 
     83 - (NSNumber*)moxNumberOfCharacters {
     84  return @([[self moxValue] length]);
     85 }
     86 
     87 - (NSString*)moxSelectedText {
     88  GeckoTextMarkerRange selection = GetSelectionInObject(self);
     89  if (!selection.IsValid()) {
     90    return nil;
     91  }
     92 
     93  return selection.Text();
     94 }
     95 
     96 - (NSValue*)moxSelectedTextRange {
     97  GeckoTextMarkerRange selection = GetSelectionInObject(self);
     98  if (!selection.IsValid()) {
     99    return nil;
    100  }
    101 
    102  GeckoTextMarkerRange fromStartToSelection(
    103      GeckoTextMarker(mGeckoAccessible, 0), selection.Start());
    104 
    105  return [NSValue valueWithRange:NSMakeRange(fromStartToSelection.Length(),
    106                                             selection.Length())];
    107 }
    108 
    109 - (NSValue*)moxVisibleCharacterRange {
    110  // XXX this won't work with Textarea and such as we actually don't give
    111  // the visible character range.
    112  return [NSValue valueWithRange:NSMakeRange(0, [[self moxValue] length])];
    113 }
    114 
    115 - (void)moxSetValue:(id)value {
    116  MOZ_ASSERT(mGeckoAccessible);
    117 
    118  nsString text;
    119  nsCocoaUtils::GetStringForNSString(value, text);
    120  if (HyperTextAccessibleBase* textAcc = mGeckoAccessible->AsHyperTextBase()) {
    121    textAcc->ReplaceText(text);
    122  }
    123 }
    124 
    125 - (void)moxSetSelectedText:(NSString*)selectedText {
    126  MOZ_ASSERT(mGeckoAccessible);
    127 
    128  NSString* stringValue = ToNSString(selectedText);
    129  if (!stringValue) {
    130    return;
    131  }
    132 
    133  HyperTextAccessibleBase* textAcc = mGeckoAccessible->AsHyperTextBase();
    134  if (!textAcc) {
    135    return;
    136  }
    137  int32_t start = 0, end = 0;
    138  textAcc->SelectionBoundsAt(0, &start, &end);
    139  nsString text;
    140  nsCocoaUtils::GetStringForNSString(stringValue, text);
    141  textAcc->SelectionBoundsAt(0, &start, &end);
    142  textAcc->DeleteText(start, end - start);
    143  textAcc->InsertText(text, start);
    144 }
    145 
    146 - (void)moxSetSelectedTextRange:(NSValue*)selectedTextRange {
    147  GeckoTextMarkerRange markerRange =
    148      GetTextMarkerRangeFromRange(self, selectedTextRange);
    149 
    150  if (markerRange.IsValid()) {
    151    markerRange.Select();
    152  }
    153 }
    154 
    155 - (void)moxSetVisibleCharacterRange:(NSValue*)visibleCharacterRange {
    156  MOZ_ASSERT(mGeckoAccessible);
    157 
    158  NSRange range;
    159  if (!ToNSRange(visibleCharacterRange, &range)) {
    160    return;
    161  }
    162 
    163  if (HyperTextAccessibleBase* textAcc = mGeckoAccessible->AsHyperTextBase()) {
    164    textAcc->ScrollSubstringTo(range.location, range.location + range.length,
    165                               nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
    166  }
    167 }
    168 
    169 - (NSString*)moxStringForRange:(NSValue*)range {
    170  GeckoTextMarkerRange markerRange = GetTextMarkerRangeFromRange(self, range);
    171 
    172  if (!markerRange.IsValid()) {
    173    return nil;
    174  }
    175 
    176  return markerRange.Text();
    177 }
    178 
    179 - (NSAttributedString*)moxAttributedStringForRange:(NSValue*)range {
    180  GeckoTextMarkerRange markerRange = GetTextMarkerRangeFromRange(self, range);
    181 
    182  if (!markerRange.IsValid()) {
    183    return nil;
    184  }
    185 
    186  return markerRange.AttributedText();
    187 }
    188 
    189 - (NSValue*)moxRangeForLine:(NSNumber*)line {
    190  // XXX: actually get the integer value for the line #
    191  return [NSValue valueWithRange:NSMakeRange(0, [[self moxValue] length])];
    192 }
    193 
    194 - (NSNumber*)moxLineForIndex:(NSNumber*)index {
    195  // XXX: actually return the line #
    196  return @0;
    197 }
    198 
    199 - (NSValue*)moxBoundsForRange:(NSValue*)range {
    200  GeckoTextMarkerRange markerRange = GetTextMarkerRangeFromRange(self, range);
    201 
    202  if (!markerRange.IsValid()) {
    203    return nil;
    204  }
    205 
    206  return markerRange.Bounds();
    207 }
    208 
    209 - (BOOL)moxIsTextField {
    210  return !mGeckoAccessible->HasNumericValue() &&
    211         mGeckoAccessible->IsEditableRoot();
    212 }
    213 
    214 - (BOOL)blockTextFieldMethod:(SEL)selector {
    215  // These are the editable text methods defined in this category.
    216  // We want to block them in certain cases.
    217  if (selector != @selector(moxNumberOfCharacters) &&
    218      selector != @selector(moxInsertionPointLineNumber) &&
    219      selector != @selector(moxSelectedText) &&
    220      selector != @selector(moxSelectedTextRange) &&
    221      selector != @selector(moxVisibleCharacterRange) &&
    222      selector != @selector(moxSetSelectedText:) &&
    223      selector != @selector(moxSetSelectedTextRange:) &&
    224      selector != @selector(moxSetVisibleCharacterRange:) &&
    225      selector != @selector(moxStringForRange:) &&
    226      selector != @selector(moxAttributedStringForRange:) &&
    227      selector != @selector(moxRangeForLine:) &&
    228      selector != @selector(moxLineForIndex:) &&
    229      selector != @selector(moxBoundsForRange:) &&
    230      selector != @selector(moxSetValue:)) {
    231    return NO;
    232  }
    233 
    234  if ([[mozAccessible class] instanceMethodForSelector:selector] !=
    235      [self methodForSelector:selector]) {
    236    // This method was overridden by a subclass, so let it through.
    237    return NO;
    238  }
    239 
    240  if (![self moxIsTextField]) {
    241    // This is not an editable root, so block these methods.
    242    return YES;
    243  }
    244 
    245  if (selector == @selector(moxSetValue:) &&
    246      [self stateWithMask:states::EDITABLE] == 0) {
    247    // The editable is read-only, so block setValue:
    248    // Bug 1995330 - should rely on READONLY/UNAVAILABLE here.
    249    return YES;
    250  }
    251 
    252  // Let these methods through.
    253  return NO;
    254 }
    255 
    256 - (void)handleAccessibleTextChangeEvent:(NSString*)change
    257                               inserted:(BOOL)isInserted
    258                            inContainer:(Accessible*)container
    259                                     at:(int32_t)start {
    260  MOZ_ASSERT([self moxIsTextField]);
    261  GeckoTextMarker startMarker(container, start);
    262  NSDictionary* userInfo = @{
    263    @"AXTextChangeElement" : self,
    264    @"AXTextStateChangeType" : @(AXTextStateChangeTypeEdit),
    265    @"AXTextChangeValues" : @[ @{
    266      @"AXTextChangeValue" : (change ? change : @""),
    267      @"AXTextChangeValueStartMarker" :
    268          (__bridge id)startMarker.CreateAXTextMarker(),
    269      @"AXTextEditType" : isInserted ? @(AXTextEditTypeTyping)
    270                                     : @(AXTextEditTypeDelete)
    271    } ]
    272  };
    273 
    274  mozAccessible* webArea = [self topWebArea];
    275  [webArea moxPostNotification:NSAccessibilityValueChangedNotification
    276                  withUserInfo:userInfo];
    277  [self moxPostNotification:NSAccessibilityValueChangedNotification
    278               withUserInfo:userInfo];
    279 
    280  [self moxPostNotification:NSAccessibilityValueChangedNotification];
    281 }
    282 
    283 @end
    284 
    285 @implementation mozTextLeafAccessible
    286 
    287 - (BOOL)moxBlockSelector:(SEL)selector {
    288  if (selector == @selector(moxChildren) || selector == @selector
    289                                                (moxTitleUIElement)) {
    290    return YES;
    291  }
    292 
    293  return [super moxBlockSelector:selector];
    294 }
    295 
    296 - (NSString*)moxValue {
    297  MOZ_ASSERT(mGeckoAccessible);
    298 
    299  nsAutoString name;
    300  mGeckoAccessible->Name(name);
    301 
    302  if (nsCoreUtils::IsWhitespaceString(name)) {
    303    return nil;
    304  }
    305 
    306  return nsCocoaUtils::ToNSString(name);
    307 }
    308 
    309 - (NSString*)moxTitle {
    310  return nil;
    311 }
    312 
    313 - (NSString*)moxLabel {
    314  return nil;
    315 }
    316 
    317 - (BOOL)moxIgnoreWithParent:(mozAccessible*)parent {
    318  // Don't render text nodes that are completely empty
    319  // or those that should be ignored based on our
    320  // standard ignore rules
    321  return [self moxValue] == nil || [super moxIgnoreWithParent:parent];
    322 }
    323 
    324 static GeckoTextMarkerRange TextMarkerSubrange(Accessible* aAccessible,
    325                                               NSValue* aRange) {
    326  GeckoTextMarkerRange textMarkerRange(aAccessible);
    327  GeckoTextMarker start = textMarkerRange.Start();
    328  GeckoTextMarker end = textMarkerRange.End();
    329 
    330  NSRange r = [aRange rangeValue];
    331  start.Offset() += r.location;
    332  end.Offset() = start.Offset() + r.length;
    333 
    334  textMarkerRange = GeckoTextMarkerRange(start, end);
    335  // Crop range to accessible
    336  textMarkerRange.Crop(aAccessible);
    337 
    338  return textMarkerRange;
    339 }
    340 
    341 - (NSString*)moxStringForRange:(NSValue*)range {
    342  MOZ_ASSERT(mGeckoAccessible);
    343  GeckoTextMarkerRange textMarkerRange =
    344      TextMarkerSubrange(mGeckoAccessible, range);
    345 
    346  return textMarkerRange.Text();
    347 }
    348 
    349 - (NSAttributedString*)moxAttributedStringForRange:(NSValue*)range {
    350  MOZ_ASSERT(mGeckoAccessible);
    351  GeckoTextMarkerRange textMarkerRange =
    352      TextMarkerSubrange(mGeckoAccessible, range);
    353 
    354  return textMarkerRange.AttributedText();
    355 }
    356 
    357 - (NSValue*)moxBoundsForRange:(NSValue*)range {
    358  MOZ_ASSERT(mGeckoAccessible);
    359  GeckoTextMarkerRange textMarkerRange =
    360      TextMarkerSubrange(mGeckoAccessible, range);
    361 
    362  return textMarkerRange.Bounds();
    363 }
    364 
    365 @end