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