accessible/src/mac/mozAccessible.mm

branch
TOR_BUG_9701
changeset 3
141e0f1194b1
equal deleted inserted replaced
-1:000000000000 0:2832aa86e659
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 "nsIAccessibleText.h"
15 #include "nsIAccessibleEditableText.h"
16 #include "nsIPersistentProperties2.h"
17 #include "Relation.h"
18 #include "Role.h"
19 #include "RootAccessible.h"
20
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"
27
28 using namespace mozilla;
29 using namespace mozilla::a11y;
30
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;
37
38 // this object is not ignored, so let's return it.
39 if (![anObject accessibilityIsIgnored])
40 return GetObjectOrRepresentedView(anObject);
41
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 }
49
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);
54
55 return unignoredObject;
56
57 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
58 }
59
60 #pragma mark -
61
62 @implementation mozAccessible
63
64 - (id)initWithAccessible:(AccessibleWrap*)geckoAccessible
65 {
66 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
67
68 if ((self = [super init])) {
69 mGeckoAccessible = geckoAccessible;
70 mRole = geckoAccessible->Role();
71 }
72
73 return self;
74
75 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
76 }
77
78 - (void)dealloc
79 {
80 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
81
82 [mChildren release];
83 [super dealloc];
84
85 NS_OBJC_END_TRY_ABORT_BLOCK;
86 }
87
88 #pragma mark -
89
90 - (BOOL)accessibilityIsIgnored
91 {
92 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
93
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));
98
99 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
100 }
101
102 - (NSArray*)accessibilityAttributeNames
103 {
104 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
105
106 // if we're expired, we don't support any attributes.
107 if (!mGeckoAccessible)
108 return [NSArray array];
109
110 static NSArray *generalAttributes = nil;
111
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 }
135
136 return generalAttributes;
137
138 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
139 }
140
141 - (id)accessibilityAttributeValue:(NSString*)attribute
142 {
143 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
144
145 if (!mGeckoAccessible)
146 return nil;
147
148 #if DEBUG
149 if ([attribute isEqualToString:@"AXMozDescription"])
150 return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
151 #endif
152
153 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
154 return [self children];
155 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
156 return [self parent];
157
158 #ifdef DEBUG_hakan
159 NSLog (@"(%@ responding to attr %@)", self, attribute);
160 #endif
161
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];
193
194 #ifdef DEBUG
195 NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
196 #endif
197 return nil;
198
199 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
200 }
201
202 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
203 {
204 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
205
206 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
207 return [self canBeFocused];
208
209 return NO;
210
211 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
212 }
213
214 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
215 {
216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
217
218 #ifdef DEBUG_hakan
219 NSLog (@"[%@] %@='%@'", self, attribute, value);
220 #endif
221
222 // we only support focusing elements so far.
223 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
224 [self focus];
225
226 NS_OBJC_END_TRY_ABORT_BLOCK;
227 }
228
229 - (id)accessibilityHitTest:(NSPoint)point
230 {
231 if (!mGeckoAccessible)
232 return nil;
233
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));
242
243 Accessible* child = mGeckoAccessible->ChildAtPoint(geckoPoint.x, geckoPoint.y,
244 Accessible::eDeepestChild);
245
246 if (child) {
247 mozAccessible* nativeChild = GetNativeFromGeckoAccessible(child);
248 if (nativeChild)
249 return GetClosestInterestingAccessible(nativeChild);
250 }
251
252 // if we didn't find anything, return ourself (or the first unignored ancestor).
253 return GetClosestInterestingAccessible(self);
254 }
255
256 - (NSArray*)accessibilityActionNames
257 {
258 return nil;
259 }
260
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 }
267
268 - (void)accessibilityPerformAction:(NSString*)action
269 {
270 }
271
272 - (id)accessibilityFocusedUIElement
273 {
274 if (!mGeckoAccessible)
275 return nil;
276
277 Accessible* focusedGeckoChild = mGeckoAccessible->FocusedChild();
278 if (focusedGeckoChild) {
279 mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
280 if (focusedChild)
281 return GetClosestInterestingAccessible(focusedChild);
282 }
283
284 // return ourself if we can't get a native focused child.
285 return GetClosestInterestingAccessible(self);
286 }
287
288 #pragma mark -
289
290 - (id <mozAccessible>)parent
291 {
292 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
293
294 Accessible* accessibleParent = mGeckoAccessible->GetUnignoredParent();
295 if (accessibleParent) {
296 id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
297 if (nativeParent)
298 return GetClosestInterestingAccessible(nativeParent);
299 }
300
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);
309
310 return GetClosestInterestingAccessible(nativeParent);
311
312 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
313 }
314
315 - (BOOL)hasRepresentedView
316 {
317 return NO;
318 }
319
320 - (id)representedView
321 {
322 return nil;
323 }
324
325 - (BOOL)isRoot
326 {
327 return NO;
328 }
329
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;
335
336 if (mChildren || !mGeckoAccessible->AreChildrenCached())
337 return mChildren;
338
339 mChildren = [[NSMutableArray alloc] init];
340
341 // get the array of children.
342 nsAutoTArray<Accessible*, 10> childrenArray;
343 mGeckoAccessible->GetUnignoredChildren(&childrenArray);
344
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 }
355
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
364
365 return mChildren;
366
367 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
368 }
369
370 - (NSValue*)position
371 {
372 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
373
374 if (!mGeckoAccessible)
375 return nil;
376
377 int32_t x = 0, y = 0, width = 0, height = 0;
378 mGeckoAccessible->GetBounds(&x, &y, &width, &height);
379
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);
384
385 return [NSValue valueWithPoint:p];
386
387 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
388 }
389
390 - (NSValue*)size
391 {
392 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
393
394 if (!mGeckoAccessible)
395 return nil;
396
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)];
403
404 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
405 }
406
407 - (NSString*)role
408 {
409 if (!mGeckoAccessible)
410 return nil;
411
412 #ifdef DEBUG_A11Y
413 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible),
414 "Does not support nsIAccessibleText when it should");
415 #endif
416
417 #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
418 case roles::geckoRole: \
419 return macRole;
420
421 switch (mRole) {
422 #include "RoleMap.h"
423 default:
424 NS_NOTREACHED("Unknown role.");
425 return NSAccessibilityUnknownRole;
426 }
427
428 #undef ROLE
429 }
430
431 - (NSString*)subrole
432 {
433 if (!mGeckoAccessible)
434 return nil;
435
436 // XXX maybe we should cache the subrole.
437 nsAutoString xmlRoles;
438
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);
443
444 nsWhitespaceTokenizer tokenizer(xmlRoles);
445
446 while (tokenizer.hasMoreTokens()) {
447 const nsDependentSubstring token(tokenizer.nextToken());
448
449 if (token.EqualsLiteral("banner"))
450 return @"AXLandmarkBanner";
451
452 if (token.EqualsLiteral("complementary"))
453 return @"AXLandmarkComplementary";
454
455 if (token.EqualsLiteral("contentinfo"))
456 return @"AXLandmarkContentInfo";
457
458 if (token.EqualsLiteral("main"))
459 return @"AXLandmarkMain";
460
461 if (token.EqualsLiteral("navigation"))
462 return @"AXLandmarkNavigation";
463
464 if (token.EqualsLiteral("search"))
465 return @"AXLandmarkSearch";
466 }
467
468 switch (mRole) {
469 case roles::LIST:
470 return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole;
471
472 case roles::DEFINITION_LIST:
473 return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
474
475 case roles::TERM:
476 return @"AXTerm";
477
478 case roles::DEFINITION:
479 return @"AXDefinition";
480
481 default:
482 break;
483 }
484
485 return nil;
486 }
487
488 - (NSString*)roleDescription
489 {
490 if (mRole == roles::DOCUMENT)
491 return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
492
493 NSString* subrole = [self subrole];
494
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"));
499
500 NSString* role = [self role];
501
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 }
517
518 return NSAccessibilityRoleDescription(role, subrole);
519 }
520
521 - (NSString*)title
522 {
523 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
524
525 nsAutoString title;
526 mGeckoAccessible->Name(title);
527 return nsCocoaUtils::ToNSString(title);
528
529 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
530 }
531
532 - (id)value
533 {
534 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
535
536 nsAutoString value;
537 mGeckoAccessible->GetValue (value);
538 return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.BeginReading())
539 length:value.Length()];
540
541 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
542 }
543
544 - (void)valueDidChange
545 {
546 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
547
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.
553
554 NS_OBJC_END_TRY_ABORT_BLOCK;
555 }
556
557 - (void)selectedTextDidChange
558 {
559 // Do nothing. mozTextAccessible will.
560 }
561
562 - (NSString*)customDescription
563 {
564 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
565
566 if (mGeckoAccessible->IsDefunct())
567 return nil;
568
569 nsAutoString desc;
570 mGeckoAccessible->Description(desc);
571
572 return nsCocoaUtils::ToNSString(desc);
573
574 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
575 }
576
577 - (NSString*)help
578 {
579 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
580
581 nsAutoString helpText;
582 mGeckoAccessible->GetHelp (helpText);
583 return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(helpText.BeginReading())
584 length:helpText.Length()];
585
586 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
587 }
588
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;
593
594 return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
595
596 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
597 }
598
599 - (BOOL)isFocused
600 {
601 return FocusMgr()->IsFocused(mGeckoAccessible);
602 }
603
604 - (BOOL)canBeFocused
605 {
606 return mGeckoAccessible && (mGeckoAccessible->InteractiveState() & states::FOCUSABLE);
607 }
608
609 - (BOOL)focus
610 {
611 if (!mGeckoAccessible)
612 return NO;
613
614 nsresult rv = mGeckoAccessible->TakeFocus();
615 return NS_SUCCEEDED(rv);
616 }
617
618 - (BOOL)isEnabled
619 {
620 return mGeckoAccessible && ((mGeckoAccessible->InteractiveState() & states::UNAVAILABLE) == 0);
621 }
622
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;
628
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);
635
636 NS_OBJC_END_TRY_ABORT_BLOCK;
637 }
638
639 - (NSWindow*)window
640 {
641 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
642
643 AccessibleWrap* accWrap = static_cast<AccessibleWrap*>(mGeckoAccessible);
644
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());
650
651 NSAssert1(nativeWindow, @"Could not get native window for %@", self);
652 return nativeWindow;
653
654 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
655 }
656
657 - (void)invalidateChildren
658 {
659 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
660
661 // make room for new children
662 [mChildren release];
663 mChildren = nil;
664
665 NS_OBJC_END_TRY_ABORT_BLOCK;
666 }
667
668 - (void)appendChild:(Accessible*)aAccessible
669 {
670 // if mChildren is nil, then we don't even need to bother
671 if (!mChildren)
672 return;
673
674 mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
675 if (curNative)
676 [mChildren addObject:GetObjectOrRepresentedView(curNative)];
677 }
678
679 - (void)expire
680 {
681 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
682
683 [self invalidateChildren];
684
685 mGeckoAccessible = nullptr;
686
687 NS_OBJC_END_TRY_ABORT_BLOCK;
688 }
689
690 - (BOOL)isExpired
691 {
692 return !mGeckoAccessible;
693 }
694
695 #pragma mark -
696 #pragma mark Debug methods
697 #pragma mark -
698
699 #ifdef DEBUG
700
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;
706
707 NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!");
708 NSEnumerator *iter = [children objectEnumerator];
709 mozAccessible *curObj = nil;
710
711 NSLog(@"sanity checking %@", self);
712
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 }
719
720 NS_OBJC_END_TRY_ABORT_BLOCK;
721 }
722
723 - (void)sanityCheckChildren
724 {
725 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
726
727 [self sanityCheckChildren:[self children]];
728
729 NS_OBJC_END_TRY_ABORT_BLOCK;
730 }
731
732 - (void)printHierarchy
733 {
734 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
735
736 [self printHierarchyWithLevel:0];
737
738 NS_OBJC_END_TRY_ABORT_BLOCK;
739 }
740
741 - (void)printHierarchyWithLevel:(unsigned)level
742 {
743 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
744
745 NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
746
747 // print this node
748 NSMutableString *indent = [NSMutableString stringWithCapacity:level];
749 unsigned i=0;
750 for (;i<level;i++)
751 [indent appendString:@" "];
752
753 NSLog (@"%@(#%i) %@", indent, level, self);
754
755 // use |children| method to make sure our children are lazily fetched first.
756 NSArray *children = [self children];
757 if (!children)
758 return;
759
760 if (![self accessibilityIsIgnored])
761 [self sanityCheckChildren];
762
763 NSEnumerator *iter = [children objectEnumerator];
764 mozAccessible *object = nil;
765
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)];
770
771 NS_OBJC_END_TRY_ABORT_BLOCK;
772 }
773
774 #endif /* DEBUG */
775
776 @end

mercurial