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/. */ 5 6#import "mozAccessible.h" 7 8#import "MacUtils.h" 9#import "mozView.h" 10 11#include "Accessible-inl.h" 12#include "nsAccUtils.h" 13#include "nsIAccessibleRelation.h" 14#include "nsIAccessibleEditableText.h" 15#include "nsIPersistentProperties2.h" 16#include "DocAccessibleParent.h" 17#include "Relation.h" 18#include "Role.h" 19#include "RootAccessible.h" 20#include "TableAccessible.h" 21#include "TableCellAccessible.h" 22#include "mozilla/a11y/PDocAccessible.h" 23#include "OuterDocAccessible.h" 24 25#include "mozilla/Services.h" 26#include "nsRect.h" 27#include "nsCocoaUtils.h" 28#include "nsCoord.h" 29#include "nsObjCExceptions.h" 30#include "nsWhitespaceTokenizer.h" 31#include <prdtoa.h> 32 33using namespace mozilla; 34using namespace mozilla::a11y; 35 36#define NSAccessibilityDOMIdentifierAttribute @"AXDOMIdentifier" 37#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand" 38#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex" 39#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator" 40#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator" 41#define NSAccessibilityMathBaseAttribute @"AXMathBase" 42#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript" 43#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript" 44#define NSAccessibilityMathUnderAttribute @"AXMathUnder" 45#define NSAccessibilityMathOverAttribute @"AXMathOver" 46#define NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness" 47// XXX WebKit also defines the following attributes. 48// See bugs 1176970 and 1176983. 49// - NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen" 50// - NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose" 51// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts" 52// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts" 53 54// convert an array of Gecko accessibles to an NSArray of native accessibles 55static inline NSMutableArray* 56ConvertToNSArray(nsTArray<Accessible*>& aArray) 57{ 58 NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; 59 60 // iterate through the list, and get each native accessible. 61 size_t totalCount = aArray.Length(); 62 for (size_t i = 0; i < totalCount; i++) { 63 Accessible* curAccessible = aArray.ElementAt(i); 64 mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); 65 if (curNative) 66 [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; 67 } 68 69 return nativeArray; 70} 71 72// convert an array of Gecko proxy accessibles to an NSArray of native accessibles 73static inline NSMutableArray* 74ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray) 75{ 76 NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; 77 78 // iterate through the list, and get each native accessible. 79 size_t totalCount = aArray.Length(); 80 for (size_t i = 0; i < totalCount; i++) { 81 ProxyAccessible* curAccessible = aArray.ElementAt(i); 82 mozAccessible* curNative = GetNativeFromProxy(curAccessible); 83 if (curNative) 84 [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; 85 } 86 87 return nativeArray; 88} 89 90#pragma mark - 91 92@implementation mozAccessible 93 94- (id)initWithAccessible:(uintptr_t)aGeckoAccessible 95{ 96 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 97 98 if ((self = [super init])) { 99 mGeckoAccessible = aGeckoAccessible; 100 if (aGeckoAccessible & IS_PROXY) 101 mRole = [self getProxyAccessible]->Role(); 102 else 103 mRole = [self getGeckoAccessible]->Role(); 104 } 105 106 return self; 107 108 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 109} 110 111- (void)dealloc 112{ 113 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 114 115 [mChildren release]; 116 [super dealloc]; 117 118 NS_OBJC_END_TRY_ABORT_BLOCK; 119} 120 121- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible 122{ 123 // Check if mGeckoAccessible points at a proxy 124 if (mGeckoAccessible & IS_PROXY) 125 return nil; 126 127 return reinterpret_cast<AccessibleWrap*>(mGeckoAccessible); 128} 129 130- (mozilla::a11y::ProxyAccessible*)getProxyAccessible 131{ 132 // Check if mGeckoAccessible points at a proxy 133 if (!(mGeckoAccessible & IS_PROXY)) 134 return nil; 135 136 return reinterpret_cast<ProxyAccessible*>(mGeckoAccessible & ~IS_PROXY); 137} 138 139#pragma mark - 140 141- (BOOL)accessibilityIsIgnored 142{ 143 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 144 145 // unknown (either unimplemented, or irrelevant) elements are marked as ignored 146 // as well as expired elements. 147 148 bool noRole = [[self role] isEqualToString:NSAccessibilityUnknownRole]; 149 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 150 return (noRole && !(accWrap->InteractiveState() & states::FOCUSABLE)); 151 152 if (ProxyAccessible* proxy = [self getProxyAccessible]) 153 return (noRole && !(proxy->State() & states::FOCUSABLE)); 154 155 return true; 156 157 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); 158} 159 160- (NSArray*)additionalAccessibilityAttributeNames 161{ 162 NSMutableArray* additional = [NSMutableArray array]; 163 [additional addObject:NSAccessibilityDOMIdentifierAttribute]; 164 switch (mRole) { 165 case roles::MATHML_ROOT: 166 [additional addObject:NSAccessibilityMathRootIndexAttribute]; 167 [additional addObject:NSAccessibilityMathRootRadicandAttribute]; 168 break; 169 case roles::MATHML_SQUARE_ROOT: 170 [additional addObject:NSAccessibilityMathRootRadicandAttribute]; 171 break; 172 case roles::MATHML_FRACTION: 173 [additional addObject:NSAccessibilityMathFractionNumeratorAttribute]; 174 [additional addObject:NSAccessibilityMathFractionDenominatorAttribute]; 175 [additional addObject:NSAccessibilityMathLineThicknessAttribute]; 176 break; 177 case roles::MATHML_SUB: 178 case roles::MATHML_SUP: 179 case roles::MATHML_SUB_SUP: 180 [additional addObject:NSAccessibilityMathBaseAttribute]; 181 [additional addObject:NSAccessibilityMathSubscriptAttribute]; 182 [additional addObject:NSAccessibilityMathSuperscriptAttribute]; 183 break; 184 case roles::MATHML_UNDER: 185 case roles::MATHML_OVER: 186 case roles::MATHML_UNDER_OVER: 187 [additional addObject:NSAccessibilityMathBaseAttribute]; 188 [additional addObject:NSAccessibilityMathUnderAttribute]; 189 [additional addObject:NSAccessibilityMathOverAttribute]; 190 break; 191 // XXX bug 1176983 192 // roles::MATHML_MULTISCRIPTS should also have the following attributes: 193 // - NSAccessibilityMathPrescriptsAttribute 194 // - NSAccessibilityMathPostscriptsAttribute 195 // XXX bug 1176970 196 // roles::MATHML_FENCED should also have the following attributes: 197 // - NSAccessibilityMathFencedOpenAttribute 198 // - NSAccessibilityMathFencedCloseAttribute 199 default: 200 break; 201 } 202 203 return additional; 204} 205 206- (NSArray*)accessibilityAttributeNames 207{ 208 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 209 210 // if we're expired, we don't support any attributes. 211 AccessibleWrap* accWrap = [self getGeckoAccessible]; 212 ProxyAccessible* proxy = [self getProxyAccessible]; 213 if (!accWrap && !proxy) 214 return [NSArray array]; 215 216 static NSArray* generalAttributes = nil; 217 218 if (!generalAttributes) { 219 // standard attributes that are shared and supported by all generic elements. 220 generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute, 221 NSAccessibilityParentAttribute, 222 NSAccessibilityRoleAttribute, 223 NSAccessibilityTitleAttribute, 224 NSAccessibilityValueAttribute, 225 NSAccessibilitySubroleAttribute, 226 NSAccessibilityRoleDescriptionAttribute, 227 NSAccessibilityPositionAttribute, 228 NSAccessibilityEnabledAttribute, 229 NSAccessibilitySizeAttribute, 230 NSAccessibilityWindowAttribute, 231 NSAccessibilityFocusedAttribute, 232 NSAccessibilityHelpAttribute, 233 NSAccessibilityTitleUIElementAttribute, 234 NSAccessibilityTopLevelUIElementAttribute, 235#if DEBUG 236 @"AXMozDescription", 237#endif 238 nil]; 239 } 240 241 NSArray* objectAttributes = generalAttributes; 242 243 NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames]; 244 if ([additionalAttributes count]) 245 objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes]; 246 247 return objectAttributes; 248 249 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 250} 251 252- (id)childAt:(uint32_t)i 253{ 254 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 255 256 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) { 257 Accessible* child = accWrap->GetChildAt(i); 258 return child ? GetNativeFromGeckoAccessible(child) : nil; 259 } else if (ProxyAccessible* proxy = [self getProxyAccessible]) { 260 ProxyAccessible* child = proxy->ChildAt(i); 261 return child ? GetNativeFromProxy(child) : nil; 262 } 263 264 return nil; 265 266 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 267} 268 269- (id)accessibilityAttributeValue:(NSString*)attribute 270{ 271 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 272 273 AccessibleWrap* accWrap = [self getGeckoAccessible]; 274 ProxyAccessible* proxy = [self getProxyAccessible]; 275 if (!accWrap && !proxy) 276 return nil; 277 278#if DEBUG 279 if ([attribute isEqualToString:@"AXMozDescription"]) 280 return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]]; 281#endif 282 283 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) 284 return [self children]; 285 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 286 return [self parent]; 287 288#ifdef DEBUG_hakan 289 NSLog (@"(%@ responding to attr %@)", self, attribute); 290#endif 291 292 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 293 return [self role]; 294 if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) 295 return [self position]; 296 if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) 297 return [self subrole]; 298 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 299 return [NSNumber numberWithBool:[self isEnabled]]; 300 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) 301 return [self value]; 302 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 303 return [self roleDescription]; 304 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 305 return [NSNumber numberWithBool:[self isFocused]]; 306 if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) 307 return [self size]; 308 if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) 309 return [self window]; 310 if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) 311 return [self window]; 312 if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) 313 return [self title]; 314 if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) { 315 if (accWrap) { 316 Relation rel = accWrap->RelationByType(RelationType::LABELLED_BY); 317 Accessible* tempAcc = rel.Next(); 318 return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil; 319 } 320 nsTArray<ProxyAccessible*> rel = proxy->RelationByType(RelationType::LABELLED_BY); 321 ProxyAccessible* tempProxy = rel.SafeElementAt(0); 322 return tempProxy ? GetNativeFromProxy(tempProxy) : nil; 323 } 324 if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) 325 return [self help]; 326 if ([attribute isEqualToString:NSAccessibilityOrientationAttribute]) 327 return [self orientation]; 328 329 if ([attribute isEqualToString:NSAccessibilityDOMIdentifierAttribute]) { 330 nsAutoString id; 331 if (accWrap) 332 nsCoreUtils::GetID(accWrap->GetContent(), id); 333 else 334 proxy->DOMNodeID(id); 335 return nsCocoaUtils::ToNSString(id); 336 } 337 338 switch (mRole) { 339 case roles::MATHML_ROOT: 340 if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute]) 341 return [self childAt:0]; 342 if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute]) 343 return [self childAt:1]; 344 break; 345 case roles::MATHML_SQUARE_ROOT: 346 if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute]) 347 return [self childAt:0]; 348 break; 349 case roles::MATHML_FRACTION: 350 if ([attribute isEqualToString:NSAccessibilityMathFractionNumeratorAttribute]) 351 return [self childAt:0]; 352 if ([attribute isEqualToString:NSAccessibilityMathFractionDenominatorAttribute]) 353 return [self childAt:1]; 354 if ([attribute isEqualToString:NSAccessibilityMathLineThicknessAttribute]) { 355 // WebKit sets line thickness to some logical value parsed in the 356 // renderer object of the <mfrac> element. It's not clear whether the 357 // exact value is relevant to assistive technologies. From a semantic 358 // point of view, the only important point is to distinguish between 359 // <mfrac> elements that have a fraction bar and those that do not. 360 // Per the MathML 3 spec, the latter happens iff the linethickness 361 // attribute is of the form [zero-float][optional-unit]. In that case we 362 // set line thickness to zero and in the other cases we set it to one. 363 nsAutoString thickness; 364 if (accWrap) { 365 nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes(); 366 nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness); 367 } else { 368 AutoTArray<Attribute, 10> attrs; 369 proxy->Attributes(&attrs); 370 for (size_t i = 0 ; i < attrs.Length() ; i++) { 371 if (attrs.ElementAt(i).Name() == "thickness") { 372 thickness = attrs.ElementAt(i).Value(); 373 break; 374 } 375 } 376 } 377 double value = 1.0; 378 if (!thickness.IsEmpty()) 379 value = PR_strtod(NS_LossyConvertUTF16toASCII(thickness).get(), 380 nullptr); 381 return [NSNumber numberWithInteger:(value ? 1 : 0)]; 382 } 383 break; 384 case roles::MATHML_SUB: 385 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 386 return [self childAt:0]; 387 if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute]) 388 return [self childAt:1]; 389#ifdef DEBUG 390 if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute]) 391 return nil; 392#endif 393 break; 394 case roles::MATHML_SUP: 395 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 396 return [self childAt:0]; 397#ifdef DEBUG 398 if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute]) 399 return nil; 400#endif 401 if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute]) 402 return [self childAt:1]; 403 break; 404 case roles::MATHML_SUB_SUP: 405 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 406 return [self childAt:0]; 407 if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute]) 408 return [self childAt:1]; 409 if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute]) 410 return [self childAt:2]; 411 break; 412 case roles::MATHML_UNDER: 413 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 414 return [self childAt:0]; 415 if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute]) 416 return [self childAt:1]; 417#ifdef DEBUG 418 if ([attribute isEqualToString:NSAccessibilityMathOverAttribute]) 419 return nil; 420#endif 421 break; 422 case roles::MATHML_OVER: 423 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 424 return [self childAt:0]; 425#ifdef DEBUG 426 if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute]) 427 return nil; 428#endif 429 if ([attribute isEqualToString:NSAccessibilityMathOverAttribute]) 430 return [self childAt:1]; 431 break; 432 case roles::MATHML_UNDER_OVER: 433 if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute]) 434 return [self childAt:0]; 435 if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute]) 436 return [self childAt:1]; 437 if ([attribute isEqualToString:NSAccessibilityMathOverAttribute]) 438 return [self childAt:2]; 439 break; 440 // XXX bug 1176983 441 // roles::MATHML_MULTISCRIPTS should also have the following attributes: 442 // - NSAccessibilityMathPrescriptsAttribute 443 // - NSAccessibilityMathPostscriptsAttribute 444 // XXX bug 1176970 445 // roles::MATHML_FENCED should also have the following attributes: 446 // - NSAccessibilityMathFencedOpenAttribute 447 // - NSAccessibilityMathFencedCloseAttribute 448 default: 449 break; 450 } 451 452#ifdef DEBUG 453 NSLog (@"!!! %@ can't respond to attribute %@", self, attribute); 454#endif 455 return nil; 456 457 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 458} 459 460- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute 461{ 462 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 463 464 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 465 return [self canBeFocused]; 466 467 return NO; 468 469 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); 470} 471 472- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute 473{ 474 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 475 476#ifdef DEBUG_hakan 477 NSLog (@"[%@] %@='%@'", self, attribute, value); 478#endif 479 480 // we only support focusing elements so far. 481 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue]) 482 [self focus]; 483 484 NS_OBJC_END_TRY_ABORT_BLOCK; 485} 486 487- (id)accessibilityHitTest:(NSPoint)point 488{ 489 AccessibleWrap* accWrap = [self getGeckoAccessible]; 490 ProxyAccessible* proxy = [self getProxyAccessible]; 491 if (!accWrap && !proxy) 492 return nil; 493 494 // Convert the given screen-global point in the cocoa coordinate system (with 495 // origin in the bottom-left corner of the screen) into point in the Gecko 496 // coordinate system (with origin in a top-left screen point). 497 NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; 498 NSPoint tmpPoint = NSMakePoint(point.x, 499 [mainView frame].size.height - point.y); 500 LayoutDeviceIntPoint geckoPoint = nsCocoaUtils:: 501 CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView)); 502 503 mozAccessible* nativeChild = nil; 504 if (accWrap) { 505 Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y, 506 Accessible::eDeepestChild); 507 if (child) 508 nativeChild = GetNativeFromGeckoAccessible(child); 509 } else if (proxy) { 510 ProxyAccessible* child = proxy->ChildAtPoint(geckoPoint.x, geckoPoint.y, 511 Accessible::eDeepestChild); 512 if (child) 513 nativeChild = GetNativeFromProxy(child); 514 } 515 516 if (nativeChild) 517 return nativeChild; 518 519 // if we didn't find anything, return ourself or child view. 520 return GetObjectOrRepresentedView(self); 521} 522 523- (NSArray*)accessibilityActionNames 524{ 525 return nil; 526} 527 528- (NSString*)accessibilityActionDescription:(NSString*)action 529{ 530 // by default we return whatever the MacOS API know about. 531 // if you have custom actions, override. 532 return NSAccessibilityActionDescription(action); 533} 534 535- (void)accessibilityPerformAction:(NSString*)action 536{ 537} 538 539- (id)accessibilityFocusedUIElement 540{ 541 AccessibleWrap* accWrap = [self getGeckoAccessible]; 542 ProxyAccessible* proxy = [self getProxyAccessible]; 543 if (!accWrap && !proxy) 544 return nil; 545 546 mozAccessible* focusedChild = nil; 547 if (accWrap) { 548 Accessible* focusedGeckoChild = accWrap->FocusedChild(); 549 if (focusedGeckoChild) 550 focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild); 551 } else if (proxy) { 552 ProxyAccessible* focusedGeckoChild = proxy->FocusedChild(); 553 if (focusedGeckoChild) 554 focusedChild = GetNativeFromProxy(focusedGeckoChild); 555 } 556 557 if (focusedChild) 558 return GetObjectOrRepresentedView(focusedChild); 559 560 // return ourself if we can't get a native focused child. 561 return GetObjectOrRepresentedView(self); 562} 563 564#pragma mark - 565 566- (id <mozAccessible>)parent 567{ 568 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 569 570 id nativeParent = nil; 571 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) { 572 Accessible* accessibleParent = accWrap->Parent(); 573 if (accessibleParent) 574 nativeParent = GetNativeFromGeckoAccessible(accessibleParent); 575 if (nativeParent) 576 return GetObjectOrRepresentedView(nativeParent); 577 578 // Return native of root accessible if we have no direct parent 579 nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible()); 580 } else if (ProxyAccessible* proxy = [self getProxyAccessible]) { 581 if (ProxyAccessible* proxyParent = proxy->Parent()) { 582 nativeParent = GetNativeFromProxy(proxyParent); 583 } 584 585 if (nativeParent) 586 return GetObjectOrRepresentedView(nativeParent); 587 588 Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser(); 589 nativeParent = outerDoc ? 590 GetNativeFromGeckoAccessible(outerDoc) : nil; 591 } else { 592 return nil; 593 } 594 595 NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self); 596 597 return GetObjectOrRepresentedView(nativeParent); 598 599 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 600} 601 602- (BOOL)hasRepresentedView 603{ 604 return NO; 605} 606 607- (id)representedView 608{ 609 return nil; 610} 611 612- (BOOL)isRoot 613{ 614 return NO; 615} 616 617// gets our native children lazily. 618// returns nil when there are no children. 619- (NSArray*)children 620{ 621 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 622 623 if (mChildren) 624 return mChildren; 625 626 // get the array of children. 627 mChildren = [[NSMutableArray alloc] init]; 628 629 AccessibleWrap* accWrap = [self getGeckoAccessible]; 630 if (accWrap) { 631 uint32_t childCount = accWrap->ChildCount(); 632 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { 633 mozAccessible* nativeChild = GetNativeFromGeckoAccessible(accWrap->GetChildAt(childIdx)); 634 if (nativeChild) 635 [mChildren addObject:nativeChild]; 636 } 637 638 // children from child if this is an outerdoc 639 OuterDocAccessible* docOwner = accWrap->AsOuterDoc(); 640 if (docOwner) { 641 if (ProxyAccessible* proxyDoc = docOwner->RemoteChildDoc()) { 642 mozAccessible* nativeRemoteChild = GetNativeFromProxy(proxyDoc); 643 [mChildren insertObject:nativeRemoteChild atIndex:0]; 644 NSAssert1 (nativeRemoteChild, @"%@ found a child remote doc missing a native\n", self); 645 } 646 } 647 } else if (ProxyAccessible* proxy = [self getProxyAccessible]) { 648 uint32_t childCount = proxy->ChildrenCount(); 649 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { 650 mozAccessible* nativeChild = GetNativeFromProxy(proxy->ChildAt(childIdx)); 651 if (nativeChild) 652 [mChildren addObject:nativeChild]; 653 } 654 655 } 656 657 return mChildren; 658 659 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 660} 661 662- (NSValue*)position 663{ 664 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 665 666 nsIntRect rect; 667 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 668 rect = accWrap->Bounds(); 669 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 670 rect = proxy->Bounds(); 671 else 672 return nil; 673 674 NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; 675 CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView); 676 NSPoint p = NSMakePoint(static_cast<CGFloat>(rect.x) / scaleFactor, 677 [mainView frame].size.height - static_cast<CGFloat>(rect.y + rect.height) / scaleFactor); 678 679 return [NSValue valueWithPoint:p]; 680 681 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 682} 683 684- (NSValue*)size 685{ 686 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 687 688 nsIntRect rect; 689 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 690 rect = accWrap->Bounds(); 691 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 692 rect = proxy->Bounds(); 693 else 694 return nil; 695 696 CGFloat scaleFactor = 697 nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]); 698 return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(rect.width) / scaleFactor, 699 static_cast<CGFloat>(rect.height) / scaleFactor)]; 700 701 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 702} 703 704- (NSString*)role 705{ 706 AccessibleWrap* accWrap = [self getGeckoAccessible]; 707 if (accWrap) { 708 #ifdef DEBUG_A11Y 709 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), 710 "Does not support Text when it should"); 711 #endif 712 } else if (![self getProxyAccessible]) { 713 return nil; 714 } 715 716#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \ 717 case roles::geckoRole: \ 718 return macRole; 719 720 switch (mRole) { 721#include "RoleMap.h" 722 default: 723 NS_NOTREACHED("Unknown role."); 724 return NSAccessibilityUnknownRole; 725 } 726 727#undef ROLE 728} 729 730- (NSString*)subrole 731{ 732 AccessibleWrap* accWrap = [self getGeckoAccessible]; 733 ProxyAccessible* proxy = [self getProxyAccessible]; 734 735 // Deal with landmarks first 736 nsAtom* landmark = nullptr; 737 if (accWrap) 738 landmark = accWrap->LandmarkRole(); 739 else if (proxy) 740 landmark = proxy->LandmarkRole(); 741 742 // HTML Elements treated as landmarks 743 // XXX bug 1371712 744 if (landmark) { 745 if (landmark == nsGkAtoms::application) 746 return @"AXLandmarkApplication"; 747 if (landmark == nsGkAtoms::banner) 748 return @"AXLandmarkBanner"; 749 if (landmark == nsGkAtoms::complementary) 750 return @"AXLandmarkComplementary"; 751 if (landmark == nsGkAtoms::contentinfo) 752 return @"AXLandmarkContentInfo"; 753 if (landmark == nsGkAtoms::form) 754 return @"AXLandmarkForm"; 755 if (landmark == nsGkAtoms::main) 756 return @"AXLandmarkMain"; 757 if (landmark == nsGkAtoms::navigation) 758 return @"AXLandmarkNavigation"; 759 if (landmark == nsGkAtoms::search) 760 return @"AXLandmarkSearch"; 761 if (landmark == nsGkAtoms::searchbox) 762 return @"AXSearchField"; 763 } 764 765 // macOS groups the specific landmark types of DPub ARIA into two broad 766 // categories with corresponding subroles: Navigation and region/container. 767 if (mRole == roles::NAVIGATION) 768 return @"AXLandmarkNavigation"; 769 if (mRole == roles::LANDMARK) 770 return @"AXLandmarkRegion"; 771 772 // Now, deal with widget roles 773 nsAtom* roleAtom = nullptr; 774 if (accWrap && accWrap->HasARIARole()) { 775 const nsRoleMapEntry* roleMap = accWrap->ARIARoleMap(); 776 roleAtom = *roleMap->roleAtom; 777 } 778 if (proxy) 779 roleAtom = proxy->ARIARoleAtom(); 780 781 if (roleAtom) { 782 if (roleAtom == nsGkAtoms::alert) 783 return @"AXApplicationAlert"; 784 if (roleAtom == nsGkAtoms::alertdialog) 785 return @"AXApplicationAlertDialog"; 786 if (roleAtom == nsGkAtoms::article) 787 return @"AXDocumentArticle"; 788 if (roleAtom == nsGkAtoms::dialog) 789 return @"AXApplicationDialog"; 790 if (roleAtom == nsGkAtoms::document) 791 return @"AXDocument"; 792 if (roleAtom == nsGkAtoms::log_) 793 return @"AXApplicationLog"; 794 if (roleAtom == nsGkAtoms::marquee) 795 return @"AXApplicationMarquee"; 796 if (roleAtom == nsGkAtoms::math) 797 return @"AXDocumentMath"; 798 if (roleAtom == nsGkAtoms::note_) 799 return @"AXDocumentNote"; 800 if (roleAtom == nsGkAtoms::region) 801 return mRole == roles::REGION ? @"AXLandmarkRegion" : nil; 802 if (roleAtom == nsGkAtoms::status) 803 return @"AXApplicationStatus"; 804 if (roleAtom == nsGkAtoms::tabpanel) 805 return @"AXTabPanel"; 806 if (roleAtom == nsGkAtoms::timer) 807 return @"AXApplicationTimer"; 808 if (roleAtom == nsGkAtoms::tooltip) 809 return @"AXUserInterfaceTooltip"; 810 } 811 812 switch (mRole) { 813 case roles::LIST: 814 return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole; 815 816 case roles::ENTRY: 817 if ((accWrap && accWrap->IsSearchbox()) || 818 (proxy && proxy->IsSearchbox())) 819 return @"AXSearchField"; 820 break; 821 822 case roles::DEFINITION_LIST: 823 return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole; 824 825 case roles::TERM: 826 return @"AXTerm"; 827 828 case roles::DEFINITION: 829 return @"AXDefinition"; 830 831 case roles::MATHML_MATH: 832 return @"AXDocumentMath"; 833 834 case roles::MATHML_FRACTION: 835 return @"AXMathFraction"; 836 837 case roles::MATHML_FENCED: 838 // XXX bug 1176970 839 // This should be AXMathFence, but doing so without implementing the 840 // whole fence interface seems to make VoiceOver crash, so we present it 841 // as a row for now. 842 return @"AXMathRow"; 843 844 case roles::MATHML_SUB: 845 case roles::MATHML_SUP: 846 case roles::MATHML_SUB_SUP: 847 return @"AXMathSubscriptSuperscript"; 848 849 case roles::MATHML_ROW: 850 case roles::MATHML_STYLE: 851 case roles::MATHML_ERROR: 852 return @"AXMathRow"; 853 854 case roles::MATHML_UNDER: 855 case roles::MATHML_OVER: 856 case roles::MATHML_UNDER_OVER: 857 return @"AXMathUnderOver"; 858 859 case roles::MATHML_SQUARE_ROOT: 860 return @"AXMathSquareRoot"; 861 862 case roles::MATHML_ROOT: 863 return @"AXMathRoot"; 864 865 case roles::MATHML_TEXT: 866 return @"AXMathText"; 867 868 case roles::MATHML_NUMBER: 869 return @"AXMathNumber"; 870 871 case roles::MATHML_IDENTIFIER: 872 return @"AXMathIdentifier"; 873 874 case roles::MATHML_TABLE: 875 return @"AXMathTable"; 876 877 case roles::MATHML_TABLE_ROW: 878 return @"AXMathTableRow"; 879 880 case roles::MATHML_CELL: 881 return @"AXMathTableCell"; 882 883 // XXX: NSAccessibility also uses subroles AXMathSeparatorOperator and 884 // AXMathFenceOperator. We should use the NS_MATHML_OPERATOR_FENCE and 885 // NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they 886 // are only available from the MathML layout code. Hence we just fallback 887 // to subrole AXMathOperator for now. 888 // XXX bug 1175747 WebKit also creates anonymous operators for <mfenced> 889 // which have subroles AXMathSeparatorOperator and AXMathFenceOperator. 890 case roles::MATHML_OPERATOR: 891 return @"AXMathOperator"; 892 893 case roles::MATHML_MULTISCRIPTS: 894 return @"AXMathMultiscript"; 895 896 case roles::SWITCH: 897 return @"AXSwitch"; 898 899 case roles::ALERT: 900 return @"AXApplicationAlert"; 901 902 case roles::PROPERTYPAGE: 903 return @"AXTabPanel"; 904 905 case roles::DETAILS: 906 return @"AXDetails"; 907 908 case roles::SUMMARY: 909 return @"AXSummary"; 910 911 case roles::NOTE: 912 return @"AXDocumentNote"; 913 914 case roles::OUTLINEITEM: 915 return @"AXOutlineRow"; 916 917 case roles::ARTICLE: 918 return @"AXDocumentArticle"; 919 920 case roles::NON_NATIVE_DOCUMENT: 921 return @"AXDocument"; 922 923 // macOS added an AXSubrole value to distinguish generic AXGroup objects 924 // from those which are AXGroups as a result of an explicit ARIA role, 925 // such as the non-landmark, non-listitem text containers in DPub ARIA. 926 case roles::FOOTNOTE: 927 case roles::SECTION: 928 if (roleAtom) 929 return @"AXApplicationGroup"; 930 break; 931 932 default: 933 break; 934 } 935 936 return nil; 937} 938 939struct RoleDescrMap 940{ 941 NSString* role; 942 const nsString description; 943}; 944 945static const RoleDescrMap sRoleDescrMap[] = { 946 { @"AXApplicationAlert", NS_LITERAL_STRING("alert") }, 947 { @"AXApplicationAlertDialog", NS_LITERAL_STRING("alertDialog") }, 948 { @"AXApplicationLog", NS_LITERAL_STRING("log") }, 949 { @"AXApplicationMarquee", NS_LITERAL_STRING("marquee") }, 950 { @"AXApplicationStatus", NS_LITERAL_STRING("status") }, 951 { @"AXApplicationTimer", NS_LITERAL_STRING("timer") }, 952 { @"AXContentSeparator", NS_LITERAL_STRING("separator") }, 953 { @"AXDefinition", NS_LITERAL_STRING("definition") }, 954 { @"AXDocument", NS_LITERAL_STRING("document") }, 955 { @"AXDocumentArticle", NS_LITERAL_STRING("article") }, 956 { @"AXDocumentMath", NS_LITERAL_STRING("math") }, 957 { @"AXDocumentNote", NS_LITERAL_STRING("note") }, 958 { @"AXLandmarkApplication", NS_LITERAL_STRING("application") }, 959 { @"AXLandmarkBanner", NS_LITERAL_STRING("banner") }, 960 { @"AXLandmarkComplementary", NS_LITERAL_STRING("complementary") }, 961 { @"AXLandmarkContentInfo", NS_LITERAL_STRING("content") }, 962 { @"AXLandmarkMain", NS_LITERAL_STRING("main") }, 963 { @"AXLandmarkNavigation", NS_LITERAL_STRING("navigation") }, 964 { @"AXLandmarkRegion", NS_LITERAL_STRING("region") }, 965 { @"AXLandmarkSearch", NS_LITERAL_STRING("search") }, 966 { @"AXSearchField", NS_LITERAL_STRING("searchTextField") }, 967 { @"AXTabPanel", NS_LITERAL_STRING("tabPanel") }, 968 { @"AXTerm", NS_LITERAL_STRING("term") }, 969 { @"AXUserInterfaceTooltip", NS_LITERAL_STRING("tooltip") } 970}; 971 972struct RoleDescrComparator 973{ 974 const NSString* mRole; 975 explicit RoleDescrComparator(const NSString* aRole) : mRole(aRole) {} 976 int operator()(const RoleDescrMap& aEntry) const { 977 return [mRole compare:aEntry.role]; 978 } 979}; 980 981- (NSString*)roleDescription 982{ 983 if (mRole == roles::DOCUMENT) 984 return utils::LocalizedString(NS_LITERAL_STRING("htmlContent")); 985 986 if (mRole == roles::FIGURE) 987 return utils::LocalizedString(NS_LITERAL_STRING("figure")); 988 989 if (mRole == roles::HEADING) 990 return utils::LocalizedString(NS_LITERAL_STRING("heading")); 991 992 NSString* subrole = [self subrole]; 993 994 if (subrole) { 995 size_t idx = 0; 996 if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap), 997 RoleDescrComparator(subrole), &idx)) { 998 return utils::LocalizedString(sRoleDescrMap[idx].description); 999 } 1000 } 1001 1002 return NSAccessibilityRoleDescription([self role], subrole); 1003} 1004 1005- (NSString*)title 1006{ 1007 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1008 1009 nsAutoString title; 1010 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1011 accWrap->Name(title); 1012 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 1013 proxy->Name(title); 1014 1015 return nsCocoaUtils::ToNSString(title); 1016 1017 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1018} 1019 1020- (id)value 1021{ 1022 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1023 1024 nsAutoString value; 1025 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1026 accWrap->Value(value); 1027 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 1028 proxy->Value(value); 1029 1030 return nsCocoaUtils::ToNSString(value); 1031 1032 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1033} 1034 1035- (void)valueDidChange 1036{ 1037 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1038 1039#ifdef DEBUG_hakan 1040 NSLog(@"%@'s value changed!", self); 1041#endif 1042 // sending out a notification is expensive, so we don't do it other than for really important objects, 1043 // like mozTextAccessible. 1044 1045 NS_OBJC_END_TRY_ABORT_BLOCK; 1046} 1047 1048- (void)selectedTextDidChange 1049{ 1050 // Do nothing. mozTextAccessible will. 1051} 1052 1053- (void)documentLoadComplete 1054{ 1055 id realSelf = GetObjectOrRepresentedView(self); 1056 NSAccessibilityPostNotification(realSelf, NSAccessibilityFocusedUIElementChangedNotification); 1057 NSAccessibilityPostNotification(realSelf, @"AXLoadComplete"); 1058 NSAccessibilityPostNotification(realSelf, @"AXLayoutComplete"); 1059} 1060 1061- (NSString*)help 1062{ 1063 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1064 1065 // What needs to go here is actually the accDescription of an item. 1066 // The MSAA acc_help method has nothing to do with this one. 1067 nsAutoString helpText; 1068 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1069 accWrap->Description(helpText); 1070 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 1071 proxy->Description(helpText); 1072 1073 return nsCocoaUtils::ToNSString(helpText); 1074 1075 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1076} 1077 1078- (NSString*)orientation 1079{ 1080 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1081 1082 uint64_t state; 1083 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1084 state = accWrap->InteractiveState(); 1085 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 1086 state = proxy->State(); 1087 else 1088 state = 0; 1089 1090 if (state & states::HORIZONTAL) 1091 return NSAccessibilityHorizontalOrientationValue; 1092 if (state & states::VERTICAL) 1093 return NSAccessibilityVerticalOrientationValue; 1094 1095 return NSAccessibilityUnknownOrientationValue; 1096 1097 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1098} 1099 1100// objc-style description (from NSObject); not to be confused with the accessible description above. 1101- (NSString*)description 1102{ 1103 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1104 1105 return [NSString stringWithFormat:@"(%p) %@", self, [self role]]; 1106 1107 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1108} 1109 1110- (BOOL)isFocused 1111{ 1112 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) { 1113 return FocusMgr()->IsFocused(accWrap); 1114 } 1115 1116 return false; //XXX: proxy implementation is needed. 1117} 1118 1119- (BOOL)canBeFocused 1120{ 1121 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1122 return accWrap->InteractiveState() & states::FOCUSABLE; 1123 1124 if (ProxyAccessible* proxy = [self getProxyAccessible]) 1125 return proxy->State() & states::FOCUSABLE; 1126 1127 return false; 1128} 1129 1130- (BOOL)focus 1131{ 1132 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1133 accWrap->TakeFocus(); 1134 else if (ProxyAccessible* proxy = [self getProxyAccessible]) 1135 proxy->TakeFocus(); 1136 else 1137 return NO; 1138 1139 return YES; 1140} 1141 1142- (BOOL)isEnabled 1143{ 1144 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) 1145 return ((accWrap->InteractiveState() & states::UNAVAILABLE) == 0); 1146 1147 if (ProxyAccessible* proxy = [self getProxyAccessible]) 1148 return ((proxy->State() & states::UNAVAILABLE) == 0); 1149 1150 return false; 1151} 1152 1153// The root accessible calls this when the focused node was 1154// changed to us. 1155- (void)didReceiveFocus 1156{ 1157 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1158 1159#ifdef DEBUG_hakan 1160 NSLog (@"%@ received focus!", self); 1161#endif 1162 NSAccessibilityPostNotification(GetObjectOrRepresentedView(self), 1163 NSAccessibilityFocusedUIElementChangedNotification); 1164 1165 NS_OBJC_END_TRY_ABORT_BLOCK; 1166} 1167 1168- (NSWindow*)window 1169{ 1170 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1171 1172 // Get a pointer to the native window (NSWindow) we reside in. 1173 NSWindow *nativeWindow = nil; 1174 DocAccessible* docAcc = nullptr; 1175 if (AccessibleWrap* accWrap = [self getGeckoAccessible]) { 1176 docAcc = accWrap->Document(); 1177 } else if (ProxyAccessible* proxy = [self getProxyAccessible]) { 1178 Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser(); 1179 if (outerDoc) 1180 docAcc = outerDoc->Document(); 1181 } 1182 1183 if (docAcc) 1184 nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow()); 1185 1186 NSAssert1(nativeWindow, @"Could not get native window for %@", self); 1187 return nativeWindow; 1188 1189 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1190} 1191 1192- (void)invalidateChildren 1193{ 1194 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1195 1196 // make room for new children 1197 [mChildren release]; 1198 mChildren = nil; 1199 1200 NS_OBJC_END_TRY_ABORT_BLOCK; 1201} 1202 1203- (void)appendChild:(Accessible*)aAccessible 1204{ 1205 // if mChildren is nil, then we don't even need to bother 1206 if (!mChildren) 1207 return; 1208 1209 mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible); 1210 if (curNative) 1211 [mChildren addObject:curNative]; 1212} 1213 1214- (void)expire 1215{ 1216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1217 1218 [self invalidateChildren]; 1219 1220 mGeckoAccessible = 0; 1221 1222 NS_OBJC_END_TRY_ABORT_BLOCK; 1223} 1224 1225- (BOOL)isExpired 1226{ 1227 return ![self getGeckoAccessible] && ![self getProxyAccessible]; 1228} 1229 1230#pragma mark - 1231#pragma mark Debug methods 1232#pragma mark - 1233 1234#ifdef DEBUG 1235 1236// will check that our children actually reference us as their 1237// parent. 1238- (void)sanityCheckChildren:(NSArray *)children 1239{ 1240 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1241 1242 NSEnumerator *iter = [children objectEnumerator]; 1243 mozAccessible *curObj = nil; 1244 1245 NSLog(@"sanity checking %@", self); 1246 1247 while ((curObj = [iter nextObject])) { 1248 id realSelf = GetObjectOrRepresentedView(self); 1249 NSLog(@"checking %@", realSelf); 1250 NSAssert2([curObj parent] == realSelf, 1251 @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf); 1252 } 1253 1254 NS_OBJC_END_TRY_ABORT_BLOCK; 1255} 1256 1257- (void)sanityCheckChildren 1258{ 1259 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1260 1261 [self sanityCheckChildren:[self children]]; 1262 1263 NS_OBJC_END_TRY_ABORT_BLOCK; 1264} 1265 1266- (void)printHierarchy 1267{ 1268 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1269 1270 [self printHierarchyWithLevel:0]; 1271 1272 NS_OBJC_END_TRY_ABORT_BLOCK; 1273} 1274 1275- (void)printHierarchyWithLevel:(unsigned)level 1276{ 1277 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1278 1279 NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!"); 1280 1281 // print this node 1282 NSMutableString *indent = [NSMutableString stringWithCapacity:level]; 1283 unsigned i=0; 1284 for (;i<level;i++) 1285 [indent appendString:@" "]; 1286 1287 NSLog (@"%@(#%i) %@", indent, level, self); 1288 1289 // use |children| method to make sure our children are lazily fetched first. 1290 NSArray *children = [self children]; 1291 if (!children) 1292 return; 1293 1294 [self sanityCheckChildren]; 1295 1296 NSEnumerator *iter = [children objectEnumerator]; 1297 mozAccessible *object = nil; 1298 1299 while (iter && (object = [iter nextObject])) 1300 // print every child node's subtree, increasing the indenting 1301 // by two for every level. 1302 [object printHierarchyWithLevel:(level+1)]; 1303 1304 NS_OBJC_END_TRY_ABORT_BLOCK; 1305} 1306 1307#endif /* DEBUG */ 1308 1309@end 1310