accessible/src/mac/mozTextAccessible.mm

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "Accessible-inl.h"
     7 #include "HyperTextAccessible-inl.h"
     8 #include "TextLeafAccessible.h"
    10 #include "nsCocoaUtils.h"
    11 #include "nsObjCExceptions.h"
    13 #import "mozTextAccessible.h"
    15 using namespace mozilla::a11y;
    17 inline bool
    18 ToNSRange(id aValue, NSRange* aRange)
    19 {
    20   NS_PRECONDITION(aRange, "aRange is nil");
    22   if ([aValue isKindOfClass:[NSValue class]] && 
    23       strcmp([(NSValue*)aValue objCType], @encode(NSRange)) == 0) {
    24     *aRange = [aValue rangeValue];
    25     return true;
    26   }
    28   return false;
    29 }
    31 inline NSString*
    32 ToNSString(id aValue)
    33 {
    34   if ([aValue isKindOfClass:[NSString class]]) {
    35     return aValue;
    36   }
    38   return nil;
    39 }
    41 @interface mozTextAccessible ()
    42 - (NSString*)subrole;
    43 - (NSString*)selectedText;
    44 - (NSValue*)selectedTextRange;
    45 - (NSValue*)visibleCharacterRange;
    46 - (long)textLength;
    47 - (BOOL)isReadOnly;
    48 - (NSNumber*)caretLineNumber;
    49 - (void)setText:(NSString*)newText;
    50 - (NSString*)text;
    51 - (NSString*)stringFromRange:(NSRange*)range;
    52 @end
    54 @implementation mozTextAccessible
    56 - (id)initWithAccessible:(AccessibleWrap*)accessible
    57 {
    58   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
    60   if ((self = [super initWithAccessible:accessible])) {
    61     mGeckoTextAccessible = accessible->AsHyperText();
    62     CallQueryInterface(accessible, &mGeckoEditableTextAccessible);
    63   }
    64   return self;
    66   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
    67 }
    69 - (BOOL)accessibilityIsIgnored
    70 {
    71   return !mGeckoAccessible;
    72 }
    74 - (NSArray*)accessibilityAttributeNames
    75 {
    76   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
    78   static NSMutableArray* supportedAttributes = nil;
    79   if (!supportedAttributes) {
    80     // text-specific attributes to supplement the standard one
    81     supportedAttributes = [[NSMutableArray alloc] initWithObjects:
    82       NSAccessibilitySelectedTextAttribute, // required
    83       NSAccessibilitySelectedTextRangeAttribute, // required
    84       NSAccessibilityNumberOfCharactersAttribute, // required
    85       NSAccessibilityVisibleCharacterRangeAttribute, // required
    86       NSAccessibilityInsertionPointLineNumberAttribute,
    87       @"AXRequired",
    88       @"AXInvalid",
    89       nil
    90     ];
    91     [supportedAttributes addObjectsFromArray:[super accessibilityAttributeNames]];
    92   }
    93   return supportedAttributes;
    95   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
    96 }
    98 - (id)accessibilityAttributeValue:(NSString*)attribute
    99 {
   100   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   102   if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
   103     return [NSNumber numberWithInt:[self textLength]];
   105   if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute])
   106     return [self caretLineNumber];
   108   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
   109     return [self selectedTextRange];
   111   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
   112     return [self selectedText];
   114   if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
   115     return @"";
   117   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
   118     // Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
   119     // object's AXSelectedText attribute. See bug 674612 for details.
   120     // Also if there is no selected text, we return the full text. 
   121     // See bug 369710 for details.
   122     if ([[self role] isEqualToString:NSAccessibilityStaticTextRole]) {
   123       NSString* selectedText = [self selectedText];
   124       return (selectedText && [selectedText length]) ? selectedText : [self text];
   125     }
   127     return [self text];
   128   }
   130   if ([attribute isEqualToString:@"AXRequired"])
   131     return [NSNumber numberWithBool:!!(mGeckoAccessible->State() & states::REQUIRED)];
   133   if ([attribute isEqualToString:@"AXInvalid"])
   134     return [NSNumber numberWithBool:!!(mGeckoAccessible->State() & states::INVALID)];
   136   if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
   137     return [self visibleCharacterRange];
   139   // let mozAccessible handle all other attributes
   140   return [super accessibilityAttributeValue:attribute];
   142   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   143 }
   145 - (NSArray*)accessibilityParameterizedAttributeNames
   146 {
   147   static NSArray* supportedParametrizedAttributes = nil;
   148   // text specific parametrized attributes
   149   if (!supportedParametrizedAttributes) {
   150     supportedParametrizedAttributes = [[NSArray alloc] initWithObjects:
   151       NSAccessibilityStringForRangeParameterizedAttribute,
   152       NSAccessibilityLineForIndexParameterizedAttribute,
   153       NSAccessibilityRangeForLineParameterizedAttribute,
   154       NSAccessibilityAttributedStringForRangeParameterizedAttribute,
   155       NSAccessibilityBoundsForRangeParameterizedAttribute,
   156 #if DEBUG
   157       NSAccessibilityRangeForPositionParameterizedAttribute,
   158       NSAccessibilityRangeForIndexParameterizedAttribute,
   159       NSAccessibilityRTFForRangeParameterizedAttribute,
   160       NSAccessibilityStyleRangeForIndexParameterizedAttribute,
   161 #endif
   162       nil
   163     ];
   164   }
   165   return supportedParametrizedAttributes;
   166 }
   168 - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
   169 {
   170   if (!mGeckoTextAccessible)
   171     return nil;
   173   if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
   174     NSRange range;
   175     if (!ToNSRange(parameter, &range)) {
   176 #if DEBUG
   177       NSLog(@"%@: range not set", attribute);
   178 #endif
   179       return @"";
   180     }
   182     return [self stringFromRange:&range];
   183   }
   185   if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute]) {
   186     // XXX: actually get the integer value for the line #
   187     return [NSValue valueWithRange:NSMakeRange(0, [self textLength])];
   188   }
   190   if ([attribute isEqualToString:NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
   191     NSRange range;
   192     if (!ToNSRange(parameter, &range)) {
   193 #if DEBUG
   194       NSLog(@"%@: range not set", attribute);
   195 #endif
   196       return @"";
   197     }
   199     return [[[NSAttributedString alloc] initWithString:[self stringFromRange:&range]] autorelease];
   200   }
   202   if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute]) {
   203     // XXX: actually return the line #
   204     return [NSNumber numberWithInt:0];
   205   }
   207   if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
   208     NSRange range;
   209     if (!ToNSRange(parameter, &range)) {
   210 #if DEBUG
   211       NSLog(@"%@:no range", attribute);
   212 #endif
   213       return nil;
   214     }
   216     int32_t start = range.location;
   217     int32_t end = start + range.length;
   218     nsIntRect bounds = mGeckoTextAccessible->TextBounds(start, end);
   220     return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)];
   221   }
   223 #if DEBUG
   224   NSLog(@"unhandled attribute:%@ forParameter:%@", attribute, parameter);
   225 #endif
   227   return nil;
   228 }
   230 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
   231 {
   232   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   234   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
   235     return ![self isReadOnly];
   237   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] ||
   238       [attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] ||
   239       [attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
   240     return YES;
   242   return [super accessibilityIsAttributeSettable:attribute];
   244   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
   245 }
   247 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
   248 {
   249   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   251   if (!mGeckoTextAccessible)
   252     return;
   254   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
   255     [self setText:ToNSString(value)];
   257     return;
   258   }
   260   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
   261     NSString* stringValue = ToNSString(value);
   262     if (!stringValue)
   263       return;
   265     int32_t start = 0, end = 0;
   266     mGeckoTextAccessible->SelectionBoundsAt(0, &start, &end);
   267     mGeckoTextAccessible->DeleteText(start, end - start);
   269     nsString text;
   270     nsCocoaUtils::GetStringForNSString(stringValue, text);
   271     mGeckoTextAccessible->InsertText(text, start);
   272   }
   274   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
   275     NSRange range;
   276     if (!ToNSRange(value, &range))
   277       return;
   279     mGeckoTextAccessible->SetSelectionBoundsAt(0, range.location,
   280                                                range.location + range.length);
   281     return;
   282   }
   284   if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
   285     NSRange range;
   286     if (!ToNSRange(value, &range))
   287       return;
   289     mGeckoTextAccessible->ScrollSubstringTo(range.location, range.location + range.length,
   290                                             nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
   291     return;
   292   } 
   294   [super accessibilitySetValue:value forAttribute:attribute];
   296   NS_OBJC_END_TRY_ABORT_BLOCK;
   297 }
   299 - (NSString*)subrole
   300 {
   301   if(mRole == roles::PASSWORD_TEXT)
   302     return NSAccessibilitySecureTextFieldSubrole;
   304   return nil;
   305 }
   307 - (void)expire
   308 {
   309   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   311   mGeckoTextAccessible = nullptr;
   312   NS_IF_RELEASE(mGeckoEditableTextAccessible);
   313   [super expire];
   315   NS_OBJC_END_TRY_ABORT_BLOCK;
   316 }
   318 #pragma mark -
   320 - (BOOL)isReadOnly
   321 {
   322   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   324   if ([[self role] isEqualToString:NSAccessibilityStaticTextRole])
   325     return YES;
   327   if (mGeckoEditableTextAccessible)
   328     return (mGeckoAccessible->State() & states::READONLY) == 0;
   330   return NO;
   332   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
   333 }
   335 - (NSNumber*)caretLineNumber
   336 {
   337   int32_t lineNumber = mGeckoTextAccessible ?
   338     mGeckoTextAccessible->CaretLineNumber() - 1 : -1;
   340   return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil;
   341 }
   343 - (void)setText:(NSString*)aNewString
   344 {
   345   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   347   if (mGeckoEditableTextAccessible) {
   348     nsString text;
   349     nsCocoaUtils::GetStringForNSString(aNewString, text);
   350     mGeckoEditableTextAccessible->SetTextContents(text);
   351   }
   353   NS_OBJC_END_TRY_ABORT_BLOCK;
   354 }
   356 - (NSString*)text
   357 {
   358   if (!mGeckoAccessible || !mGeckoTextAccessible)
   359     return nil;
   361   // A password text field returns an empty value
   362   if (mRole == roles::PASSWORD_TEXT)
   363     return @"";
   365   nsAutoString text;
   366   mGeckoTextAccessible->TextSubstring(0,
   367                                       nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT,
   368                                       text);
   369   return nsCocoaUtils::ToNSString(text);
   370 }
   372 - (long)textLength
   373 {
   374   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   376   if (!mGeckoAccessible || !mGeckoTextAccessible)
   377     return 0;
   379   return mGeckoTextAccessible ? mGeckoTextAccessible->CharacterCount() : 0;
   381   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
   382 }
   384 - (long)selectedTextLength
   385 {
   386   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   388   if (mGeckoTextAccessible) {
   389     int32_t start = 0, end = 0;
   390     mGeckoTextAccessible->SelectionBoundsAt(0, &start, &end);
   391     return (end - start);
   392   }
   393   return 0;
   395   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
   396 }
   398 - (NSString*)selectedText
   399 {
   400   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   402   if (mGeckoTextAccessible) {
   403     int32_t start = 0, end = 0;
   404     mGeckoTextAccessible->SelectionBoundsAt(0, &start, &end);
   405     if (start != end) {
   406       nsAutoString selText;
   407       mGeckoTextAccessible->TextSubstring(start, end, selText);
   408       return nsCocoaUtils::ToNSString(selText);
   409     }
   410   }
   411   return nil;
   413   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   414 }
   416 - (NSValue*)selectedTextRange
   417 {
   418   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   420   if (mGeckoTextAccessible) {
   421     int32_t start = 0;
   422     int32_t end = 0;
   423     int32_t count = mGeckoTextAccessible->SelectionCount();
   425     if (count) {
   426       mGeckoTextAccessible->SelectionBoundsAt(0, &start, &end);
   427       return [NSValue valueWithRange:NSMakeRange(start, end - start)];
   428     }
   430     start = mGeckoTextAccessible->CaretOffset();
   431     return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)]; 
   432   }
   433   return [NSValue valueWithRange:NSMakeRange(0, 0)];
   435   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   436 }
   438 - (NSValue*)visibleCharacterRange
   439 {
   440   // XXX this won't work with Textarea and such as we actually don't give
   441   // the visible character range.
   442   return [NSValue valueWithRange:
   443     NSMakeRange(0, mGeckoTextAccessible ? 
   444                 mGeckoTextAccessible->CharacterCount() : 0)];
   445 }
   447 - (void)valueDidChange
   448 {
   449   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   451   NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
   452                                   NSAccessibilityValueChangedNotification);
   454   NS_OBJC_END_TRY_ABORT_BLOCK;
   455 }
   457 - (void)selectedTextDidChange
   458 {
   459   NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
   460                                   NSAccessibilitySelectedTextChangedNotification);
   461 }
   463 - (NSString*)stringFromRange:(NSRange*)range
   464 {
   465   NS_PRECONDITION(mGeckoTextAccessible && range, "no Gecko text accessible or range");
   467   nsAutoString text;
   468   mGeckoTextAccessible->TextSubstring(range->location,
   469                                       range->location + range->length, text);
   470   return nsCocoaUtils::ToNSString(text);
   471 }
   473 @end
   475 @implementation mozTextLeafAccessible
   477 - (NSArray*)accessibilityAttributeNames
   478 {
   479   static NSMutableArray* supportedAttributes = nil;
   480   if (!supportedAttributes) {
   481     supportedAttributes = [[super accessibilityAttributeNames] mutableCopy];
   482     [supportedAttributes removeObject:NSAccessibilityChildrenAttribute];
   483   }
   485   return supportedAttributes;
   486 }
   488 - (id)accessibilityAttributeValue:(NSString*)attribute
   489 {
   490   if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
   491     return @"";
   493   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
   494     return [self text];
   496   return [super accessibilityAttributeValue:attribute];
   497 }
   499 - (NSString*)text
   500 {
   501   if (!mGeckoAccessible)
   502     return nil;
   504   return nsCocoaUtils::ToNSString(mGeckoAccessible->AsTextLeaf()->Text());
   505 }
   507 - (long)textLength
   508 {
   509   if (!mGeckoAccessible)
   510     return 0;
   512   return mGeckoAccessible->AsTextLeaf()->Text().Length();
   513 }
   515 @end

mercurial