1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/mac/mozAccessible.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,776 @@ 1.4 +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#import "mozAccessible.h" 1.10 + 1.11 +#import "MacUtils.h" 1.12 +#import "mozView.h" 1.13 + 1.14 +#include "Accessible-inl.h" 1.15 +#include "nsAccUtils.h" 1.16 +#include "nsIAccessibleRelation.h" 1.17 +#include "nsIAccessibleText.h" 1.18 +#include "nsIAccessibleEditableText.h" 1.19 +#include "nsIPersistentProperties2.h" 1.20 +#include "Relation.h" 1.21 +#include "Role.h" 1.22 +#include "RootAccessible.h" 1.23 + 1.24 +#include "mozilla/Services.h" 1.25 +#include "nsRect.h" 1.26 +#include "nsCocoaUtils.h" 1.27 +#include "nsCoord.h" 1.28 +#include "nsObjCExceptions.h" 1.29 +#include "nsWhitespaceTokenizer.h" 1.30 + 1.31 +using namespace mozilla; 1.32 +using namespace mozilla::a11y; 1.33 + 1.34 +// returns the passed in object if it is not ignored. if it's ignored, will return 1.35 +// the first unignored ancestor. 1.36 +static inline id 1.37 +GetClosestInterestingAccessible(id anObject) 1.38 +{ 1.39 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.40 + 1.41 + // this object is not ignored, so let's return it. 1.42 + if (![anObject accessibilityIsIgnored]) 1.43 + return GetObjectOrRepresentedView(anObject); 1.44 + 1.45 + // find the closest ancestor that is not ignored. 1.46 + id unignoredObject = anObject; 1.47 + while ((unignoredObject = [unignoredObject accessibilityAttributeValue:NSAccessibilityParentAttribute])) { 1.48 + if (![unignoredObject accessibilityIsIgnored]) 1.49 + // object is not ignored, so let's stop the search. 1.50 + break; 1.51 + } 1.52 + 1.53 + // if it's a mozAccessible, we need to take care to maybe return the view we 1.54 + // represent, to the AT. 1.55 + if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)]) 1.56 + return GetObjectOrRepresentedView(unignoredObject); 1.57 + 1.58 + return unignoredObject; 1.59 + 1.60 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.61 +} 1.62 + 1.63 +#pragma mark - 1.64 + 1.65 +@implementation mozAccessible 1.66 + 1.67 +- (id)initWithAccessible:(AccessibleWrap*)geckoAccessible 1.68 +{ 1.69 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.70 + 1.71 + if ((self = [super init])) { 1.72 + mGeckoAccessible = geckoAccessible; 1.73 + mRole = geckoAccessible->Role(); 1.74 + } 1.75 + 1.76 + return self; 1.77 + 1.78 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.79 +} 1.80 + 1.81 +- (void)dealloc 1.82 +{ 1.83 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.84 + 1.85 + [mChildren release]; 1.86 + [super dealloc]; 1.87 + 1.88 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.89 +} 1.90 + 1.91 +#pragma mark - 1.92 + 1.93 +- (BOOL)accessibilityIsIgnored 1.94 +{ 1.95 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.96 + 1.97 + // unknown (either unimplemented, or irrelevant) elements are marked as ignored 1.98 + // as well as expired elements. 1.99 + return !mGeckoAccessible || ([[self role] isEqualToString:NSAccessibilityUnknownRole] && 1.100 + !(mGeckoAccessible->InteractiveState() & states::FOCUSABLE)); 1.101 + 1.102 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); 1.103 +} 1.104 + 1.105 +- (NSArray*)accessibilityAttributeNames 1.106 +{ 1.107 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.108 + 1.109 + // if we're expired, we don't support any attributes. 1.110 + if (!mGeckoAccessible) 1.111 + return [NSArray array]; 1.112 + 1.113 + static NSArray *generalAttributes = nil; 1.114 + 1.115 + if (!generalAttributes) { 1.116 + // standard attributes that are shared and supported by all generic elements. 1.117 + generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute, 1.118 + NSAccessibilityParentAttribute, 1.119 + NSAccessibilityRoleAttribute, 1.120 + NSAccessibilityTitleAttribute, 1.121 + NSAccessibilityValueAttribute, 1.122 + NSAccessibilitySubroleAttribute, 1.123 + NSAccessibilityRoleDescriptionAttribute, 1.124 + NSAccessibilityPositionAttribute, 1.125 + NSAccessibilityEnabledAttribute, 1.126 + NSAccessibilitySizeAttribute, 1.127 + NSAccessibilityWindowAttribute, 1.128 + NSAccessibilityFocusedAttribute, 1.129 + NSAccessibilityHelpAttribute, 1.130 + NSAccessibilityTitleUIElementAttribute, 1.131 + NSAccessibilityTopLevelUIElementAttribute, 1.132 + NSAccessibilityDescriptionAttribute, 1.133 +#if DEBUG 1.134 + @"AXMozDescription", 1.135 +#endif 1.136 + nil]; 1.137 + } 1.138 + 1.139 + return generalAttributes; 1.140 + 1.141 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.142 +} 1.143 + 1.144 +- (id)accessibilityAttributeValue:(NSString*)attribute 1.145 +{ 1.146 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.147 + 1.148 + if (!mGeckoAccessible) 1.149 + return nil; 1.150 + 1.151 +#if DEBUG 1.152 + if ([attribute isEqualToString:@"AXMozDescription"]) 1.153 + return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]]; 1.154 +#endif 1.155 + 1.156 + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) 1.157 + return [self children]; 1.158 + if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 1.159 + return [self parent]; 1.160 + 1.161 +#ifdef DEBUG_hakan 1.162 + NSLog (@"(%@ responding to attr %@)", self, attribute); 1.163 +#endif 1.164 + 1.165 + if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 1.166 + return [self role]; 1.167 + if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) 1.168 + return [self position]; 1.169 + if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) 1.170 + return [self subrole]; 1.171 + if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 1.172 + return [NSNumber numberWithBool:[self isEnabled]]; 1.173 + if ([attribute isEqualToString:NSAccessibilityValueAttribute]) 1.174 + return [self value]; 1.175 + if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 1.176 + return [self roleDescription]; 1.177 + if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) 1.178 + return [self customDescription]; 1.179 + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 1.180 + return [NSNumber numberWithBool:[self isFocused]]; 1.181 + if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) 1.182 + return [self size]; 1.183 + if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) 1.184 + return [self window]; 1.185 + if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) 1.186 + return [self window]; 1.187 + if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) 1.188 + return [self title]; 1.189 + if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) { 1.190 + Relation rel = mGeckoAccessible->RelationByType(RelationType::LABELLED_BY); 1.191 + Accessible* tempAcc = rel.Next(); 1.192 + return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil; 1.193 + } 1.194 + if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) 1.195 + return [self help]; 1.196 + 1.197 +#ifdef DEBUG 1.198 + NSLog (@"!!! %@ can't respond to attribute %@", self, attribute); 1.199 +#endif 1.200 + return nil; 1.201 + 1.202 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.203 +} 1.204 + 1.205 +- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute 1.206 +{ 1.207 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.208 + 1.209 + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 1.210 + return [self canBeFocused]; 1.211 + 1.212 + return NO; 1.213 + 1.214 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); 1.215 +} 1.216 + 1.217 +- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute 1.218 +{ 1.219 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.220 + 1.221 +#ifdef DEBUG_hakan 1.222 + NSLog (@"[%@] %@='%@'", self, attribute, value); 1.223 +#endif 1.224 + 1.225 + // we only support focusing elements so far. 1.226 + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue]) 1.227 + [self focus]; 1.228 + 1.229 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.230 +} 1.231 + 1.232 +- (id)accessibilityHitTest:(NSPoint)point 1.233 +{ 1.234 + if (!mGeckoAccessible) 1.235 + return nil; 1.236 + 1.237 + // Convert the given screen-global point in the cocoa coordinate system (with 1.238 + // origin in the bottom-left corner of the screen) into point in the Gecko 1.239 + // coordinate system (with origin in a top-left screen point). 1.240 + NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; 1.241 + NSPoint tmpPoint = NSMakePoint(point.x, 1.242 + [mainView frame].size.height - point.y); 1.243 + nsIntPoint geckoPoint = nsCocoaUtils:: 1.244 + CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView)); 1.245 + 1.246 + Accessible* child = mGeckoAccessible->ChildAtPoint(geckoPoint.x, geckoPoint.y, 1.247 + Accessible::eDeepestChild); 1.248 + 1.249 + if (child) { 1.250 + mozAccessible* nativeChild = GetNativeFromGeckoAccessible(child); 1.251 + if (nativeChild) 1.252 + return GetClosestInterestingAccessible(nativeChild); 1.253 + } 1.254 + 1.255 + // if we didn't find anything, return ourself (or the first unignored ancestor). 1.256 + return GetClosestInterestingAccessible(self); 1.257 +} 1.258 + 1.259 +- (NSArray*)accessibilityActionNames 1.260 +{ 1.261 + return nil; 1.262 +} 1.263 + 1.264 +- (NSString*)accessibilityActionDescription:(NSString*)action 1.265 +{ 1.266 + // by default we return whatever the MacOS API know about. 1.267 + // if you have custom actions, override. 1.268 + return NSAccessibilityActionDescription(action); 1.269 +} 1.270 + 1.271 +- (void)accessibilityPerformAction:(NSString*)action 1.272 +{ 1.273 +} 1.274 + 1.275 +- (id)accessibilityFocusedUIElement 1.276 +{ 1.277 + if (!mGeckoAccessible) 1.278 + return nil; 1.279 + 1.280 + Accessible* focusedGeckoChild = mGeckoAccessible->FocusedChild(); 1.281 + if (focusedGeckoChild) { 1.282 + mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild); 1.283 + if (focusedChild) 1.284 + return GetClosestInterestingAccessible(focusedChild); 1.285 + } 1.286 + 1.287 + // return ourself if we can't get a native focused child. 1.288 + return GetClosestInterestingAccessible(self); 1.289 +} 1.290 + 1.291 +#pragma mark - 1.292 + 1.293 +- (id <mozAccessible>)parent 1.294 +{ 1.295 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.296 + 1.297 + Accessible* accessibleParent = mGeckoAccessible->GetUnignoredParent(); 1.298 + if (accessibleParent) { 1.299 + id nativeParent = GetNativeFromGeckoAccessible(accessibleParent); 1.300 + if (nativeParent) 1.301 + return GetClosestInterestingAccessible(nativeParent); 1.302 + } 1.303 + 1.304 + // GetUnignoredParent() returns null when there is no unignored accessible all the way up to 1.305 + // the root accessible. so we'll have to return whatever native accessible is above our root accessible 1.306 + // (which might be the owning NSWindow in the application, for example). 1.307 + // 1.308 + // get the native root accessible, and tell it to return its first parent unignored accessible. 1.309 + RootAccessible* root = mGeckoAccessible->RootAccessible(); 1.310 + id nativeParent = GetNativeFromGeckoAccessible(static_cast<nsIAccessible*>(root)); 1.311 + NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self); 1.312 + 1.313 + return GetClosestInterestingAccessible(nativeParent); 1.314 + 1.315 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.316 +} 1.317 + 1.318 +- (BOOL)hasRepresentedView 1.319 +{ 1.320 + return NO; 1.321 +} 1.322 + 1.323 +- (id)representedView 1.324 +{ 1.325 + return nil; 1.326 +} 1.327 + 1.328 +- (BOOL)isRoot 1.329 +{ 1.330 + return NO; 1.331 +} 1.332 + 1.333 +// gets our native children lazily. 1.334 +// returns nil when there are no children. 1.335 +- (NSArray*)children 1.336 +{ 1.337 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.338 + 1.339 + if (mChildren || !mGeckoAccessible->AreChildrenCached()) 1.340 + return mChildren; 1.341 + 1.342 + mChildren = [[NSMutableArray alloc] init]; 1.343 + 1.344 + // get the array of children. 1.345 + nsAutoTArray<Accessible*, 10> childrenArray; 1.346 + mGeckoAccessible->GetUnignoredChildren(&childrenArray); 1.347 + 1.348 + // now iterate through the children array, and get each native accessible. 1.349 + uint32_t totalCount = childrenArray.Length(); 1.350 + for (uint32_t idx = 0; idx < totalCount; idx++) { 1.351 + Accessible* curAccessible = childrenArray.ElementAt(idx); 1.352 + if (curAccessible) { 1.353 + mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible); 1.354 + if (curNative) 1.355 + [mChildren addObject:GetObjectOrRepresentedView(curNative)]; 1.356 + } 1.357 + } 1.358 + 1.359 +#ifdef DEBUG_hakan 1.360 + // make sure we're not returning any ignored accessibles. 1.361 + NSEnumerator *e = [mChildren objectEnumerator]; 1.362 + mozAccessible *m = nil; 1.363 + while ((m = [e nextObject])) { 1.364 + NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m); 1.365 + } 1.366 +#endif 1.367 + 1.368 + return mChildren; 1.369 + 1.370 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.371 +} 1.372 + 1.373 +- (NSValue*)position 1.374 +{ 1.375 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.376 + 1.377 + if (!mGeckoAccessible) 1.378 + return nil; 1.379 + 1.380 + int32_t x = 0, y = 0, width = 0, height = 0; 1.381 + mGeckoAccessible->GetBounds(&x, &y, &width, &height); 1.382 + 1.383 + NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; 1.384 + CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView); 1.385 + NSPoint p = NSMakePoint(static_cast<CGFloat>(x) / scaleFactor, 1.386 + [mainView frame].size.height - static_cast<CGFloat>(y + height) / scaleFactor); 1.387 + 1.388 + return [NSValue valueWithPoint:p]; 1.389 + 1.390 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.391 +} 1.392 + 1.393 +- (NSValue*)size 1.394 +{ 1.395 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.396 + 1.397 + if (!mGeckoAccessible) 1.398 + return nil; 1.399 + 1.400 + int32_t x = 0, y = 0, width = 0, height = 0; 1.401 + mGeckoAccessible->GetBounds (&x, &y, &width, &height); 1.402 + CGFloat scaleFactor = 1.403 + nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]); 1.404 + return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(width) / scaleFactor, 1.405 + static_cast<CGFloat>(height) / scaleFactor)]; 1.406 + 1.407 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.408 +} 1.409 + 1.410 +- (NSString*)role 1.411 +{ 1.412 + if (!mGeckoAccessible) 1.413 + return nil; 1.414 + 1.415 +#ifdef DEBUG_A11Y 1.416 + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible), 1.417 + "Does not support nsIAccessibleText when it should"); 1.418 +#endif 1.419 + 1.420 +#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \ 1.421 + case roles::geckoRole: \ 1.422 + return macRole; 1.423 + 1.424 + switch (mRole) { 1.425 +#include "RoleMap.h" 1.426 + default: 1.427 + NS_NOTREACHED("Unknown role."); 1.428 + return NSAccessibilityUnknownRole; 1.429 + } 1.430 + 1.431 +#undef ROLE 1.432 +} 1.433 + 1.434 +- (NSString*)subrole 1.435 +{ 1.436 + if (!mGeckoAccessible) 1.437 + return nil; 1.438 + 1.439 + // XXX maybe we should cache the subrole. 1.440 + nsAutoString xmlRoles; 1.441 + 1.442 + // XXX we don't need all the attributes (see bug 771113) 1.443 + nsCOMPtr<nsIPersistentProperties> attributes = mGeckoAccessible->Attributes(); 1.444 + if (attributes) 1.445 + nsAccUtils::GetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles); 1.446 + 1.447 + nsWhitespaceTokenizer tokenizer(xmlRoles); 1.448 + 1.449 + while (tokenizer.hasMoreTokens()) { 1.450 + const nsDependentSubstring token(tokenizer.nextToken()); 1.451 + 1.452 + if (token.EqualsLiteral("banner")) 1.453 + return @"AXLandmarkBanner"; 1.454 + 1.455 + if (token.EqualsLiteral("complementary")) 1.456 + return @"AXLandmarkComplementary"; 1.457 + 1.458 + if (token.EqualsLiteral("contentinfo")) 1.459 + return @"AXLandmarkContentInfo"; 1.460 + 1.461 + if (token.EqualsLiteral("main")) 1.462 + return @"AXLandmarkMain"; 1.463 + 1.464 + if (token.EqualsLiteral("navigation")) 1.465 + return @"AXLandmarkNavigation"; 1.466 + 1.467 + if (token.EqualsLiteral("search")) 1.468 + return @"AXLandmarkSearch"; 1.469 + } 1.470 + 1.471 + switch (mRole) { 1.472 + case roles::LIST: 1.473 + return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole; 1.474 + 1.475 + case roles::DEFINITION_LIST: 1.476 + return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole; 1.477 + 1.478 + case roles::TERM: 1.479 + return @"AXTerm"; 1.480 + 1.481 + case roles::DEFINITION: 1.482 + return @"AXDefinition"; 1.483 + 1.484 + default: 1.485 + break; 1.486 + } 1.487 + 1.488 + return nil; 1.489 +} 1.490 + 1.491 +- (NSString*)roleDescription 1.492 +{ 1.493 + if (mRole == roles::DOCUMENT) 1.494 + return utils::LocalizedString(NS_LITERAL_STRING("htmlContent")); 1.495 + 1.496 + NSString* subrole = [self subrole]; 1.497 + 1.498 + if ((mRole == roles::LISTITEM) && [subrole isEqualToString:@"AXTerm"]) 1.499 + return utils::LocalizedString(NS_LITERAL_STRING("term")); 1.500 + if ((mRole == roles::PARAGRAPH) && [subrole isEqualToString:@"AXDefinition"]) 1.501 + return utils::LocalizedString(NS_LITERAL_STRING("definition")); 1.502 + 1.503 + NSString* role = [self role]; 1.504 + 1.505 + // the WAI-ARIA Landmarks 1.506 + if ([role isEqualToString:NSAccessibilityGroupRole]) { 1.507 + if ([subrole isEqualToString:@"AXLandmarkBanner"]) 1.508 + return utils::LocalizedString(NS_LITERAL_STRING("banner")); 1.509 + if ([subrole isEqualToString:@"AXLandmarkComplementary"]) 1.510 + return utils::LocalizedString(NS_LITERAL_STRING("complementary")); 1.511 + if ([subrole isEqualToString:@"AXLandmarkContentInfo"]) 1.512 + return utils::LocalizedString(NS_LITERAL_STRING("content")); 1.513 + if ([subrole isEqualToString:@"AXLandmarkMain"]) 1.514 + return utils::LocalizedString(NS_LITERAL_STRING("main")); 1.515 + if ([subrole isEqualToString:@"AXLandmarkNavigation"]) 1.516 + return utils::LocalizedString(NS_LITERAL_STRING("navigation")); 1.517 + if ([subrole isEqualToString:@"AXLandmarkSearch"]) 1.518 + return utils::LocalizedString(NS_LITERAL_STRING("search")); 1.519 + } 1.520 + 1.521 + return NSAccessibilityRoleDescription(role, subrole); 1.522 +} 1.523 + 1.524 +- (NSString*)title 1.525 +{ 1.526 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.527 + 1.528 + nsAutoString title; 1.529 + mGeckoAccessible->Name(title); 1.530 + return nsCocoaUtils::ToNSString(title); 1.531 + 1.532 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.533 +} 1.534 + 1.535 +- (id)value 1.536 +{ 1.537 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.538 + 1.539 + nsAutoString value; 1.540 + mGeckoAccessible->GetValue (value); 1.541 + return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.BeginReading()) 1.542 + length:value.Length()]; 1.543 + 1.544 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.545 +} 1.546 + 1.547 +- (void)valueDidChange 1.548 +{ 1.549 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.550 + 1.551 +#ifdef DEBUG_hakan 1.552 + NSLog(@"%@'s value changed!", self); 1.553 +#endif 1.554 + // sending out a notification is expensive, so we don't do it other than for really important objects, 1.555 + // like mozTextAccessible. 1.556 + 1.557 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.558 +} 1.559 + 1.560 +- (void)selectedTextDidChange 1.561 +{ 1.562 + // Do nothing. mozTextAccessible will. 1.563 +} 1.564 + 1.565 +- (NSString*)customDescription 1.566 +{ 1.567 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.568 + 1.569 + if (mGeckoAccessible->IsDefunct()) 1.570 + return nil; 1.571 + 1.572 + nsAutoString desc; 1.573 + mGeckoAccessible->Description(desc); 1.574 + 1.575 + return nsCocoaUtils::ToNSString(desc); 1.576 + 1.577 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.578 +} 1.579 + 1.580 +- (NSString*)help 1.581 +{ 1.582 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.583 + 1.584 + nsAutoString helpText; 1.585 + mGeckoAccessible->GetHelp (helpText); 1.586 + return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(helpText.BeginReading()) 1.587 + length:helpText.Length()]; 1.588 + 1.589 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.590 +} 1.591 + 1.592 +// objc-style description (from NSObject); not to be confused with the accessible description above. 1.593 +- (NSString*)description 1.594 +{ 1.595 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.596 + 1.597 + return [NSString stringWithFormat:@"(%p) %@", self, [self role]]; 1.598 + 1.599 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.600 +} 1.601 + 1.602 +- (BOOL)isFocused 1.603 +{ 1.604 + return FocusMgr()->IsFocused(mGeckoAccessible); 1.605 +} 1.606 + 1.607 +- (BOOL)canBeFocused 1.608 +{ 1.609 + return mGeckoAccessible && (mGeckoAccessible->InteractiveState() & states::FOCUSABLE); 1.610 +} 1.611 + 1.612 +- (BOOL)focus 1.613 +{ 1.614 + if (!mGeckoAccessible) 1.615 + return NO; 1.616 + 1.617 + nsresult rv = mGeckoAccessible->TakeFocus(); 1.618 + return NS_SUCCEEDED(rv); 1.619 +} 1.620 + 1.621 +- (BOOL)isEnabled 1.622 +{ 1.623 + return mGeckoAccessible && ((mGeckoAccessible->InteractiveState() & states::UNAVAILABLE) == 0); 1.624 +} 1.625 + 1.626 +// The root accessible calls this when the focused node was 1.627 +// changed to us. 1.628 +- (void)didReceiveFocus 1.629 +{ 1.630 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.631 + 1.632 +#ifdef DEBUG_hakan 1.633 + NSLog (@"%@ received focus!", self); 1.634 +#endif 1.635 + NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self); 1.636 + NSAccessibilityPostNotification(GetObjectOrRepresentedView(self), 1.637 + NSAccessibilityFocusedUIElementChangedNotification); 1.638 + 1.639 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.640 +} 1.641 + 1.642 +- (NSWindow*)window 1.643 +{ 1.644 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.645 + 1.646 + AccessibleWrap* accWrap = static_cast<AccessibleWrap*>(mGeckoAccessible); 1.647 + 1.648 + // Get a pointer to the native window (NSWindow) we reside in. 1.649 + NSWindow *nativeWindow = nil; 1.650 + DocAccessible* docAcc = accWrap->Document(); 1.651 + if (docAcc) 1.652 + nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow()); 1.653 + 1.654 + NSAssert1(nativeWindow, @"Could not get native window for %@", self); 1.655 + return nativeWindow; 1.656 + 1.657 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.658 +} 1.659 + 1.660 +- (void)invalidateChildren 1.661 +{ 1.662 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.663 + 1.664 + // make room for new children 1.665 + [mChildren release]; 1.666 + mChildren = nil; 1.667 + 1.668 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.669 +} 1.670 + 1.671 +- (void)appendChild:(Accessible*)aAccessible 1.672 +{ 1.673 + // if mChildren is nil, then we don't even need to bother 1.674 + if (!mChildren) 1.675 + return; 1.676 + 1.677 + mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible); 1.678 + if (curNative) 1.679 + [mChildren addObject:GetObjectOrRepresentedView(curNative)]; 1.680 +} 1.681 + 1.682 +- (void)expire 1.683 +{ 1.684 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.685 + 1.686 + [self invalidateChildren]; 1.687 + 1.688 + mGeckoAccessible = nullptr; 1.689 + 1.690 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.691 +} 1.692 + 1.693 +- (BOOL)isExpired 1.694 +{ 1.695 + return !mGeckoAccessible; 1.696 +} 1.697 + 1.698 +#pragma mark - 1.699 +#pragma mark Debug methods 1.700 +#pragma mark - 1.701 + 1.702 +#ifdef DEBUG 1.703 + 1.704 +// will check that our children actually reference us as their 1.705 +// parent. 1.706 +- (void)sanityCheckChildren:(NSArray *)children 1.707 +{ 1.708 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.709 + 1.710 + NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!"); 1.711 + NSEnumerator *iter = [children objectEnumerator]; 1.712 + mozAccessible *curObj = nil; 1.713 + 1.714 + NSLog(@"sanity checking %@", self); 1.715 + 1.716 + while ((curObj = [iter nextObject])) { 1.717 + id realSelf = GetObjectOrRepresentedView(self); 1.718 + NSLog(@"checking %@", realSelf); 1.719 + NSAssert2([curObj parent] == realSelf, 1.720 + @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf); 1.721 + } 1.722 + 1.723 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.724 +} 1.725 + 1.726 +- (void)sanityCheckChildren 1.727 +{ 1.728 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.729 + 1.730 + [self sanityCheckChildren:[self children]]; 1.731 + 1.732 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.733 +} 1.734 + 1.735 +- (void)printHierarchy 1.736 +{ 1.737 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.738 + 1.739 + [self printHierarchyWithLevel:0]; 1.740 + 1.741 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.742 +} 1.743 + 1.744 +- (void)printHierarchyWithLevel:(unsigned)level 1.745 +{ 1.746 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.747 + 1.748 + NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!"); 1.749 + 1.750 + // print this node 1.751 + NSMutableString *indent = [NSMutableString stringWithCapacity:level]; 1.752 + unsigned i=0; 1.753 + for (;i<level;i++) 1.754 + [indent appendString:@" "]; 1.755 + 1.756 + NSLog (@"%@(#%i) %@", indent, level, self); 1.757 + 1.758 + // use |children| method to make sure our children are lazily fetched first. 1.759 + NSArray *children = [self children]; 1.760 + if (!children) 1.761 + return; 1.762 + 1.763 + if (![self accessibilityIsIgnored]) 1.764 + [self sanityCheckChildren]; 1.765 + 1.766 + NSEnumerator *iter = [children objectEnumerator]; 1.767 + mozAccessible *object = nil; 1.768 + 1.769 + while (iter && (object = [iter nextObject])) 1.770 + // print every child node's subtree, increasing the indenting 1.771 + // by two for every level. 1.772 + [object printHierarchyWithLevel:(level+1)]; 1.773 + 1.774 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.775 +} 1.776 + 1.777 +#endif /* DEBUG */ 1.778 + 1.779 +@end