accessible/src/mac/mozAccessible.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.

michael@0 1 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #import "mozAccessible.h"
michael@0 7
michael@0 8 #import "MacUtils.h"
michael@0 9 #import "mozView.h"
michael@0 10
michael@0 11 #include "Accessible-inl.h"
michael@0 12 #include "nsAccUtils.h"
michael@0 13 #include "nsIAccessibleRelation.h"
michael@0 14 #include "nsIAccessibleText.h"
michael@0 15 #include "nsIAccessibleEditableText.h"
michael@0 16 #include "nsIPersistentProperties2.h"
michael@0 17 #include "Relation.h"
michael@0 18 #include "Role.h"
michael@0 19 #include "RootAccessible.h"
michael@0 20
michael@0 21 #include "mozilla/Services.h"
michael@0 22 #include "nsRect.h"
michael@0 23 #include "nsCocoaUtils.h"
michael@0 24 #include "nsCoord.h"
michael@0 25 #include "nsObjCExceptions.h"
michael@0 26 #include "nsWhitespaceTokenizer.h"
michael@0 27
michael@0 28 using namespace mozilla;
michael@0 29 using namespace mozilla::a11y;
michael@0 30
michael@0 31 // returns the passed in object if it is not ignored. if it's ignored, will return
michael@0 32 // the first unignored ancestor.
michael@0 33 static inline id
michael@0 34 GetClosestInterestingAccessible(id anObject)
michael@0 35 {
michael@0 36 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 37
michael@0 38 // this object is not ignored, so let's return it.
michael@0 39 if (![anObject accessibilityIsIgnored])
michael@0 40 return GetObjectOrRepresentedView(anObject);
michael@0 41
michael@0 42 // find the closest ancestor that is not ignored.
michael@0 43 id unignoredObject = anObject;
michael@0 44 while ((unignoredObject = [unignoredObject accessibilityAttributeValue:NSAccessibilityParentAttribute])) {
michael@0 45 if (![unignoredObject accessibilityIsIgnored])
michael@0 46 // object is not ignored, so let's stop the search.
michael@0 47 break;
michael@0 48 }
michael@0 49
michael@0 50 // if it's a mozAccessible, we need to take care to maybe return the view we
michael@0 51 // represent, to the AT.
michael@0 52 if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)])
michael@0 53 return GetObjectOrRepresentedView(unignoredObject);
michael@0 54
michael@0 55 return unignoredObject;
michael@0 56
michael@0 57 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 58 }
michael@0 59
michael@0 60 #pragma mark -
michael@0 61
michael@0 62 @implementation mozAccessible
michael@0 63
michael@0 64 - (id)initWithAccessible:(AccessibleWrap*)geckoAccessible
michael@0 65 {
michael@0 66 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 67
michael@0 68 if ((self = [super init])) {
michael@0 69 mGeckoAccessible = geckoAccessible;
michael@0 70 mRole = geckoAccessible->Role();
michael@0 71 }
michael@0 72
michael@0 73 return self;
michael@0 74
michael@0 75 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 76 }
michael@0 77
michael@0 78 - (void)dealloc
michael@0 79 {
michael@0 80 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 81
michael@0 82 [mChildren release];
michael@0 83 [super dealloc];
michael@0 84
michael@0 85 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 86 }
michael@0 87
michael@0 88 #pragma mark -
michael@0 89
michael@0 90 - (BOOL)accessibilityIsIgnored
michael@0 91 {
michael@0 92 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 93
michael@0 94 // unknown (either unimplemented, or irrelevant) elements are marked as ignored
michael@0 95 // as well as expired elements.
michael@0 96 return !mGeckoAccessible || ([[self role] isEqualToString:NSAccessibilityUnknownRole] &&
michael@0 97 !(mGeckoAccessible->InteractiveState() & states::FOCUSABLE));
michael@0 98
michael@0 99 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
michael@0 100 }
michael@0 101
michael@0 102 - (NSArray*)accessibilityAttributeNames
michael@0 103 {
michael@0 104 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 105
michael@0 106 // if we're expired, we don't support any attributes.
michael@0 107 if (!mGeckoAccessible)
michael@0 108 return [NSArray array];
michael@0 109
michael@0 110 static NSArray *generalAttributes = nil;
michael@0 111
michael@0 112 if (!generalAttributes) {
michael@0 113 // standard attributes that are shared and supported by all generic elements.
michael@0 114 generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
michael@0 115 NSAccessibilityParentAttribute,
michael@0 116 NSAccessibilityRoleAttribute,
michael@0 117 NSAccessibilityTitleAttribute,
michael@0 118 NSAccessibilityValueAttribute,
michael@0 119 NSAccessibilitySubroleAttribute,
michael@0 120 NSAccessibilityRoleDescriptionAttribute,
michael@0 121 NSAccessibilityPositionAttribute,
michael@0 122 NSAccessibilityEnabledAttribute,
michael@0 123 NSAccessibilitySizeAttribute,
michael@0 124 NSAccessibilityWindowAttribute,
michael@0 125 NSAccessibilityFocusedAttribute,
michael@0 126 NSAccessibilityHelpAttribute,
michael@0 127 NSAccessibilityTitleUIElementAttribute,
michael@0 128 NSAccessibilityTopLevelUIElementAttribute,
michael@0 129 NSAccessibilityDescriptionAttribute,
michael@0 130 #if DEBUG
michael@0 131 @"AXMozDescription",
michael@0 132 #endif
michael@0 133 nil];
michael@0 134 }
michael@0 135
michael@0 136 return generalAttributes;
michael@0 137
michael@0 138 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 139 }
michael@0 140
michael@0 141 - (id)accessibilityAttributeValue:(NSString*)attribute
michael@0 142 {
michael@0 143 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 144
michael@0 145 if (!mGeckoAccessible)
michael@0 146 return nil;
michael@0 147
michael@0 148 #if DEBUG
michael@0 149 if ([attribute isEqualToString:@"AXMozDescription"])
michael@0 150 return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
michael@0 151 #endif
michael@0 152
michael@0 153 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
michael@0 154 return [self children];
michael@0 155 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
michael@0 156 return [self parent];
michael@0 157
michael@0 158 #ifdef DEBUG_hakan
michael@0 159 NSLog (@"(%@ responding to attr %@)", self, attribute);
michael@0 160 #endif
michael@0 161
michael@0 162 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
michael@0 163 return [self role];
michael@0 164 if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
michael@0 165 return [self position];
michael@0 166 if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
michael@0 167 return [self subrole];
michael@0 168 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
michael@0 169 return [NSNumber numberWithBool:[self isEnabled]];
michael@0 170 if ([attribute isEqualToString:NSAccessibilityValueAttribute])
michael@0 171 return [self value];
michael@0 172 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
michael@0 173 return [self roleDescription];
michael@0 174 if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute])
michael@0 175 return [self customDescription];
michael@0 176 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
michael@0 177 return [NSNumber numberWithBool:[self isFocused]];
michael@0 178 if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
michael@0 179 return [self size];
michael@0 180 if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
michael@0 181 return [self window];
michael@0 182 if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
michael@0 183 return [self window];
michael@0 184 if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
michael@0 185 return [self title];
michael@0 186 if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
michael@0 187 Relation rel = mGeckoAccessible->RelationByType(RelationType::LABELLED_BY);
michael@0 188 Accessible* tempAcc = rel.Next();
michael@0 189 return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
michael@0 190 }
michael@0 191 if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
michael@0 192 return [self help];
michael@0 193
michael@0 194 #ifdef DEBUG
michael@0 195 NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
michael@0 196 #endif
michael@0 197 return nil;
michael@0 198
michael@0 199 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 200 }
michael@0 201
michael@0 202 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
michael@0 203 {
michael@0 204 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 205
michael@0 206 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
michael@0 207 return [self canBeFocused];
michael@0 208
michael@0 209 return NO;
michael@0 210
michael@0 211 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
michael@0 212 }
michael@0 213
michael@0 214 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
michael@0 215 {
michael@0 216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 217
michael@0 218 #ifdef DEBUG_hakan
michael@0 219 NSLog (@"[%@] %@='%@'", self, attribute, value);
michael@0 220 #endif
michael@0 221
michael@0 222 // we only support focusing elements so far.
michael@0 223 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
michael@0 224 [self focus];
michael@0 225
michael@0 226 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 227 }
michael@0 228
michael@0 229 - (id)accessibilityHitTest:(NSPoint)point
michael@0 230 {
michael@0 231 if (!mGeckoAccessible)
michael@0 232 return nil;
michael@0 233
michael@0 234 // Convert the given screen-global point in the cocoa coordinate system (with
michael@0 235 // origin in the bottom-left corner of the screen) into point in the Gecko
michael@0 236 // coordinate system (with origin in a top-left screen point).
michael@0 237 NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
michael@0 238 NSPoint tmpPoint = NSMakePoint(point.x,
michael@0 239 [mainView frame].size.height - point.y);
michael@0 240 nsIntPoint geckoPoint = nsCocoaUtils::
michael@0 241 CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
michael@0 242
michael@0 243 Accessible* child = mGeckoAccessible->ChildAtPoint(geckoPoint.x, geckoPoint.y,
michael@0 244 Accessible::eDeepestChild);
michael@0 245
michael@0 246 if (child) {
michael@0 247 mozAccessible* nativeChild = GetNativeFromGeckoAccessible(child);
michael@0 248 if (nativeChild)
michael@0 249 return GetClosestInterestingAccessible(nativeChild);
michael@0 250 }
michael@0 251
michael@0 252 // if we didn't find anything, return ourself (or the first unignored ancestor).
michael@0 253 return GetClosestInterestingAccessible(self);
michael@0 254 }
michael@0 255
michael@0 256 - (NSArray*)accessibilityActionNames
michael@0 257 {
michael@0 258 return nil;
michael@0 259 }
michael@0 260
michael@0 261 - (NSString*)accessibilityActionDescription:(NSString*)action
michael@0 262 {
michael@0 263 // by default we return whatever the MacOS API know about.
michael@0 264 // if you have custom actions, override.
michael@0 265 return NSAccessibilityActionDescription(action);
michael@0 266 }
michael@0 267
michael@0 268 - (void)accessibilityPerformAction:(NSString*)action
michael@0 269 {
michael@0 270 }
michael@0 271
michael@0 272 - (id)accessibilityFocusedUIElement
michael@0 273 {
michael@0 274 if (!mGeckoAccessible)
michael@0 275 return nil;
michael@0 276
michael@0 277 Accessible* focusedGeckoChild = mGeckoAccessible->FocusedChild();
michael@0 278 if (focusedGeckoChild) {
michael@0 279 mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
michael@0 280 if (focusedChild)
michael@0 281 return GetClosestInterestingAccessible(focusedChild);
michael@0 282 }
michael@0 283
michael@0 284 // return ourself if we can't get a native focused child.
michael@0 285 return GetClosestInterestingAccessible(self);
michael@0 286 }
michael@0 287
michael@0 288 #pragma mark -
michael@0 289
michael@0 290 - (id <mozAccessible>)parent
michael@0 291 {
michael@0 292 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 293
michael@0 294 Accessible* accessibleParent = mGeckoAccessible->GetUnignoredParent();
michael@0 295 if (accessibleParent) {
michael@0 296 id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
michael@0 297 if (nativeParent)
michael@0 298 return GetClosestInterestingAccessible(nativeParent);
michael@0 299 }
michael@0 300
michael@0 301 // GetUnignoredParent() returns null when there is no unignored accessible all the way up to
michael@0 302 // the root accessible. so we'll have to return whatever native accessible is above our root accessible
michael@0 303 // (which might be the owning NSWindow in the application, for example).
michael@0 304 //
michael@0 305 // get the native root accessible, and tell it to return its first parent unignored accessible.
michael@0 306 RootAccessible* root = mGeckoAccessible->RootAccessible();
michael@0 307 id nativeParent = GetNativeFromGeckoAccessible(static_cast<nsIAccessible*>(root));
michael@0 308 NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
michael@0 309
michael@0 310 return GetClosestInterestingAccessible(nativeParent);
michael@0 311
michael@0 312 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 313 }
michael@0 314
michael@0 315 - (BOOL)hasRepresentedView
michael@0 316 {
michael@0 317 return NO;
michael@0 318 }
michael@0 319
michael@0 320 - (id)representedView
michael@0 321 {
michael@0 322 return nil;
michael@0 323 }
michael@0 324
michael@0 325 - (BOOL)isRoot
michael@0 326 {
michael@0 327 return NO;
michael@0 328 }
michael@0 329
michael@0 330 // gets our native children lazily.
michael@0 331 // returns nil when there are no children.
michael@0 332 - (NSArray*)children
michael@0 333 {
michael@0 334 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 335
michael@0 336 if (mChildren || !mGeckoAccessible->AreChildrenCached())
michael@0 337 return mChildren;
michael@0 338
michael@0 339 mChildren = [[NSMutableArray alloc] init];
michael@0 340
michael@0 341 // get the array of children.
michael@0 342 nsAutoTArray<Accessible*, 10> childrenArray;
michael@0 343 mGeckoAccessible->GetUnignoredChildren(&childrenArray);
michael@0 344
michael@0 345 // now iterate through the children array, and get each native accessible.
michael@0 346 uint32_t totalCount = childrenArray.Length();
michael@0 347 for (uint32_t idx = 0; idx < totalCount; idx++) {
michael@0 348 Accessible* curAccessible = childrenArray.ElementAt(idx);
michael@0 349 if (curAccessible) {
michael@0 350 mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible);
michael@0 351 if (curNative)
michael@0 352 [mChildren addObject:GetObjectOrRepresentedView(curNative)];
michael@0 353 }
michael@0 354 }
michael@0 355
michael@0 356 #ifdef DEBUG_hakan
michael@0 357 // make sure we're not returning any ignored accessibles.
michael@0 358 NSEnumerator *e = [mChildren objectEnumerator];
michael@0 359 mozAccessible *m = nil;
michael@0 360 while ((m = [e nextObject])) {
michael@0 361 NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m);
michael@0 362 }
michael@0 363 #endif
michael@0 364
michael@0 365 return mChildren;
michael@0 366
michael@0 367 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 368 }
michael@0 369
michael@0 370 - (NSValue*)position
michael@0 371 {
michael@0 372 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 373
michael@0 374 if (!mGeckoAccessible)
michael@0 375 return nil;
michael@0 376
michael@0 377 int32_t x = 0, y = 0, width = 0, height = 0;
michael@0 378 mGeckoAccessible->GetBounds(&x, &y, &width, &height);
michael@0 379
michael@0 380 NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
michael@0 381 CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
michael@0 382 NSPoint p = NSMakePoint(static_cast<CGFloat>(x) / scaleFactor,
michael@0 383 [mainView frame].size.height - static_cast<CGFloat>(y + height) / scaleFactor);
michael@0 384
michael@0 385 return [NSValue valueWithPoint:p];
michael@0 386
michael@0 387 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 388 }
michael@0 389
michael@0 390 - (NSValue*)size
michael@0 391 {
michael@0 392 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 393
michael@0 394 if (!mGeckoAccessible)
michael@0 395 return nil;
michael@0 396
michael@0 397 int32_t x = 0, y = 0, width = 0, height = 0;
michael@0 398 mGeckoAccessible->GetBounds (&x, &y, &width, &height);
michael@0 399 CGFloat scaleFactor =
michael@0 400 nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]);
michael@0 401 return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(width) / scaleFactor,
michael@0 402 static_cast<CGFloat>(height) / scaleFactor)];
michael@0 403
michael@0 404 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 405 }
michael@0 406
michael@0 407 - (NSString*)role
michael@0 408 {
michael@0 409 if (!mGeckoAccessible)
michael@0 410 return nil;
michael@0 411
michael@0 412 #ifdef DEBUG_A11Y
michael@0 413 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible),
michael@0 414 "Does not support nsIAccessibleText when it should");
michael@0 415 #endif
michael@0 416
michael@0 417 #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
michael@0 418 case roles::geckoRole: \
michael@0 419 return macRole;
michael@0 420
michael@0 421 switch (mRole) {
michael@0 422 #include "RoleMap.h"
michael@0 423 default:
michael@0 424 NS_NOTREACHED("Unknown role.");
michael@0 425 return NSAccessibilityUnknownRole;
michael@0 426 }
michael@0 427
michael@0 428 #undef ROLE
michael@0 429 }
michael@0 430
michael@0 431 - (NSString*)subrole
michael@0 432 {
michael@0 433 if (!mGeckoAccessible)
michael@0 434 return nil;
michael@0 435
michael@0 436 // XXX maybe we should cache the subrole.
michael@0 437 nsAutoString xmlRoles;
michael@0 438
michael@0 439 // XXX we don't need all the attributes (see bug 771113)
michael@0 440 nsCOMPtr<nsIPersistentProperties> attributes = mGeckoAccessible->Attributes();
michael@0 441 if (attributes)
michael@0 442 nsAccUtils::GetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles);
michael@0 443
michael@0 444 nsWhitespaceTokenizer tokenizer(xmlRoles);
michael@0 445
michael@0 446 while (tokenizer.hasMoreTokens()) {
michael@0 447 const nsDependentSubstring token(tokenizer.nextToken());
michael@0 448
michael@0 449 if (token.EqualsLiteral("banner"))
michael@0 450 return @"AXLandmarkBanner";
michael@0 451
michael@0 452 if (token.EqualsLiteral("complementary"))
michael@0 453 return @"AXLandmarkComplementary";
michael@0 454
michael@0 455 if (token.EqualsLiteral("contentinfo"))
michael@0 456 return @"AXLandmarkContentInfo";
michael@0 457
michael@0 458 if (token.EqualsLiteral("main"))
michael@0 459 return @"AXLandmarkMain";
michael@0 460
michael@0 461 if (token.EqualsLiteral("navigation"))
michael@0 462 return @"AXLandmarkNavigation";
michael@0 463
michael@0 464 if (token.EqualsLiteral("search"))
michael@0 465 return @"AXLandmarkSearch";
michael@0 466 }
michael@0 467
michael@0 468 switch (mRole) {
michael@0 469 case roles::LIST:
michael@0 470 return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole;
michael@0 471
michael@0 472 case roles::DEFINITION_LIST:
michael@0 473 return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
michael@0 474
michael@0 475 case roles::TERM:
michael@0 476 return @"AXTerm";
michael@0 477
michael@0 478 case roles::DEFINITION:
michael@0 479 return @"AXDefinition";
michael@0 480
michael@0 481 default:
michael@0 482 break;
michael@0 483 }
michael@0 484
michael@0 485 return nil;
michael@0 486 }
michael@0 487
michael@0 488 - (NSString*)roleDescription
michael@0 489 {
michael@0 490 if (mRole == roles::DOCUMENT)
michael@0 491 return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
michael@0 492
michael@0 493 NSString* subrole = [self subrole];
michael@0 494
michael@0 495 if ((mRole == roles::LISTITEM) && [subrole isEqualToString:@"AXTerm"])
michael@0 496 return utils::LocalizedString(NS_LITERAL_STRING("term"));
michael@0 497 if ((mRole == roles::PARAGRAPH) && [subrole isEqualToString:@"AXDefinition"])
michael@0 498 return utils::LocalizedString(NS_LITERAL_STRING("definition"));
michael@0 499
michael@0 500 NSString* role = [self role];
michael@0 501
michael@0 502 // the WAI-ARIA Landmarks
michael@0 503 if ([role isEqualToString:NSAccessibilityGroupRole]) {
michael@0 504 if ([subrole isEqualToString:@"AXLandmarkBanner"])
michael@0 505 return utils::LocalizedString(NS_LITERAL_STRING("banner"));
michael@0 506 if ([subrole isEqualToString:@"AXLandmarkComplementary"])
michael@0 507 return utils::LocalizedString(NS_LITERAL_STRING("complementary"));
michael@0 508 if ([subrole isEqualToString:@"AXLandmarkContentInfo"])
michael@0 509 return utils::LocalizedString(NS_LITERAL_STRING("content"));
michael@0 510 if ([subrole isEqualToString:@"AXLandmarkMain"])
michael@0 511 return utils::LocalizedString(NS_LITERAL_STRING("main"));
michael@0 512 if ([subrole isEqualToString:@"AXLandmarkNavigation"])
michael@0 513 return utils::LocalizedString(NS_LITERAL_STRING("navigation"));
michael@0 514 if ([subrole isEqualToString:@"AXLandmarkSearch"])
michael@0 515 return utils::LocalizedString(NS_LITERAL_STRING("search"));
michael@0 516 }
michael@0 517
michael@0 518 return NSAccessibilityRoleDescription(role, subrole);
michael@0 519 }
michael@0 520
michael@0 521 - (NSString*)title
michael@0 522 {
michael@0 523 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 524
michael@0 525 nsAutoString title;
michael@0 526 mGeckoAccessible->Name(title);
michael@0 527 return nsCocoaUtils::ToNSString(title);
michael@0 528
michael@0 529 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 530 }
michael@0 531
michael@0 532 - (id)value
michael@0 533 {
michael@0 534 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 535
michael@0 536 nsAutoString value;
michael@0 537 mGeckoAccessible->GetValue (value);
michael@0 538 return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.BeginReading())
michael@0 539 length:value.Length()];
michael@0 540
michael@0 541 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 542 }
michael@0 543
michael@0 544 - (void)valueDidChange
michael@0 545 {
michael@0 546 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 547
michael@0 548 #ifdef DEBUG_hakan
michael@0 549 NSLog(@"%@'s value changed!", self);
michael@0 550 #endif
michael@0 551 // sending out a notification is expensive, so we don't do it other than for really important objects,
michael@0 552 // like mozTextAccessible.
michael@0 553
michael@0 554 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 555 }
michael@0 556
michael@0 557 - (void)selectedTextDidChange
michael@0 558 {
michael@0 559 // Do nothing. mozTextAccessible will.
michael@0 560 }
michael@0 561
michael@0 562 - (NSString*)customDescription
michael@0 563 {
michael@0 564 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 565
michael@0 566 if (mGeckoAccessible->IsDefunct())
michael@0 567 return nil;
michael@0 568
michael@0 569 nsAutoString desc;
michael@0 570 mGeckoAccessible->Description(desc);
michael@0 571
michael@0 572 return nsCocoaUtils::ToNSString(desc);
michael@0 573
michael@0 574 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 575 }
michael@0 576
michael@0 577 - (NSString*)help
michael@0 578 {
michael@0 579 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 580
michael@0 581 nsAutoString helpText;
michael@0 582 mGeckoAccessible->GetHelp (helpText);
michael@0 583 return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(helpText.BeginReading())
michael@0 584 length:helpText.Length()];
michael@0 585
michael@0 586 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 587 }
michael@0 588
michael@0 589 // objc-style description (from NSObject); not to be confused with the accessible description above.
michael@0 590 - (NSString*)description
michael@0 591 {
michael@0 592 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 593
michael@0 594 return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
michael@0 595
michael@0 596 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 597 }
michael@0 598
michael@0 599 - (BOOL)isFocused
michael@0 600 {
michael@0 601 return FocusMgr()->IsFocused(mGeckoAccessible);
michael@0 602 }
michael@0 603
michael@0 604 - (BOOL)canBeFocused
michael@0 605 {
michael@0 606 return mGeckoAccessible && (mGeckoAccessible->InteractiveState() & states::FOCUSABLE);
michael@0 607 }
michael@0 608
michael@0 609 - (BOOL)focus
michael@0 610 {
michael@0 611 if (!mGeckoAccessible)
michael@0 612 return NO;
michael@0 613
michael@0 614 nsresult rv = mGeckoAccessible->TakeFocus();
michael@0 615 return NS_SUCCEEDED(rv);
michael@0 616 }
michael@0 617
michael@0 618 - (BOOL)isEnabled
michael@0 619 {
michael@0 620 return mGeckoAccessible && ((mGeckoAccessible->InteractiveState() & states::UNAVAILABLE) == 0);
michael@0 621 }
michael@0 622
michael@0 623 // The root accessible calls this when the focused node was
michael@0 624 // changed to us.
michael@0 625 - (void)didReceiveFocus
michael@0 626 {
michael@0 627 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 628
michael@0 629 #ifdef DEBUG_hakan
michael@0 630 NSLog (@"%@ received focus!", self);
michael@0 631 #endif
michael@0 632 NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self);
michael@0 633 NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
michael@0 634 NSAccessibilityFocusedUIElementChangedNotification);
michael@0 635
michael@0 636 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 637 }
michael@0 638
michael@0 639 - (NSWindow*)window
michael@0 640 {
michael@0 641 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 642
michael@0 643 AccessibleWrap* accWrap = static_cast<AccessibleWrap*>(mGeckoAccessible);
michael@0 644
michael@0 645 // Get a pointer to the native window (NSWindow) we reside in.
michael@0 646 NSWindow *nativeWindow = nil;
michael@0 647 DocAccessible* docAcc = accWrap->Document();
michael@0 648 if (docAcc)
michael@0 649 nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
michael@0 650
michael@0 651 NSAssert1(nativeWindow, @"Could not get native window for %@", self);
michael@0 652 return nativeWindow;
michael@0 653
michael@0 654 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 655 }
michael@0 656
michael@0 657 - (void)invalidateChildren
michael@0 658 {
michael@0 659 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 660
michael@0 661 // make room for new children
michael@0 662 [mChildren release];
michael@0 663 mChildren = nil;
michael@0 664
michael@0 665 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 666 }
michael@0 667
michael@0 668 - (void)appendChild:(Accessible*)aAccessible
michael@0 669 {
michael@0 670 // if mChildren is nil, then we don't even need to bother
michael@0 671 if (!mChildren)
michael@0 672 return;
michael@0 673
michael@0 674 mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
michael@0 675 if (curNative)
michael@0 676 [mChildren addObject:GetObjectOrRepresentedView(curNative)];
michael@0 677 }
michael@0 678
michael@0 679 - (void)expire
michael@0 680 {
michael@0 681 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 682
michael@0 683 [self invalidateChildren];
michael@0 684
michael@0 685 mGeckoAccessible = nullptr;
michael@0 686
michael@0 687 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 688 }
michael@0 689
michael@0 690 - (BOOL)isExpired
michael@0 691 {
michael@0 692 return !mGeckoAccessible;
michael@0 693 }
michael@0 694
michael@0 695 #pragma mark -
michael@0 696 #pragma mark Debug methods
michael@0 697 #pragma mark -
michael@0 698
michael@0 699 #ifdef DEBUG
michael@0 700
michael@0 701 // will check that our children actually reference us as their
michael@0 702 // parent.
michael@0 703 - (void)sanityCheckChildren:(NSArray *)children
michael@0 704 {
michael@0 705 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 706
michael@0 707 NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!");
michael@0 708 NSEnumerator *iter = [children objectEnumerator];
michael@0 709 mozAccessible *curObj = nil;
michael@0 710
michael@0 711 NSLog(@"sanity checking %@", self);
michael@0 712
michael@0 713 while ((curObj = [iter nextObject])) {
michael@0 714 id realSelf = GetObjectOrRepresentedView(self);
michael@0 715 NSLog(@"checking %@", realSelf);
michael@0 716 NSAssert2([curObj parent] == realSelf,
michael@0 717 @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf);
michael@0 718 }
michael@0 719
michael@0 720 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 721 }
michael@0 722
michael@0 723 - (void)sanityCheckChildren
michael@0 724 {
michael@0 725 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 726
michael@0 727 [self sanityCheckChildren:[self children]];
michael@0 728
michael@0 729 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 730 }
michael@0 731
michael@0 732 - (void)printHierarchy
michael@0 733 {
michael@0 734 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 735
michael@0 736 [self printHierarchyWithLevel:0];
michael@0 737
michael@0 738 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 739 }
michael@0 740
michael@0 741 - (void)printHierarchyWithLevel:(unsigned)level
michael@0 742 {
michael@0 743 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 744
michael@0 745 NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
michael@0 746
michael@0 747 // print this node
michael@0 748 NSMutableString *indent = [NSMutableString stringWithCapacity:level];
michael@0 749 unsigned i=0;
michael@0 750 for (;i<level;i++)
michael@0 751 [indent appendString:@" "];
michael@0 752
michael@0 753 NSLog (@"%@(#%i) %@", indent, level, self);
michael@0 754
michael@0 755 // use |children| method to make sure our children are lazily fetched first.
michael@0 756 NSArray *children = [self children];
michael@0 757 if (!children)
michael@0 758 return;
michael@0 759
michael@0 760 if (![self accessibilityIsIgnored])
michael@0 761 [self sanityCheckChildren];
michael@0 762
michael@0 763 NSEnumerator *iter = [children objectEnumerator];
michael@0 764 mozAccessible *object = nil;
michael@0 765
michael@0 766 while (iter && (object = [iter nextObject]))
michael@0 767 // print every child node's subtree, increasing the indenting
michael@0 768 // by two for every level.
michael@0 769 [object printHierarchyWithLevel:(level+1)];
michael@0 770
michael@0 771 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 772 }
michael@0 773
michael@0 774 #endif /* DEBUG */
michael@0 775
michael@0 776 @end

mercurial