1 /*
   2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // External Java Accessibility links:
  27 //
  28 // <http://java.sun.com/j2se/1.4.2/docs/guide/access/index.html>
  29 // <http://www-106.ibm.com/developerworks/library/j-access/?n-j-10172>
  30 // <http://archives.java.sun.com/archives/java-access.html> (Sun's mailing list for Java accessibility)
  31 
  32 #import "JavaComponentAccessibility.h"
  33 
  34 #import "sun_lwawt_macosx_CAccessibility.h"
  35 
  36 #import <AppKit/AppKit.h>
  37 
  38 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  39 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
  40 
  41 #import <dlfcn.h>
  42 
  43 #import "JavaAccessibilityAction.h"
  44 #import "JavaAccessibilityUtilities.h"
  45 #import "JavaTextAccessibility.h"
  46 #import "ThreadUtilities.h"
  47 #import "AWTView.h"
  48 
  49 
  50 // these constants are duplicated in CAccessibility.java
  51 #define JAVA_AX_ALL_CHILDREN (-1)
  52 #define JAVA_AX_SELECTED_CHILDREN (-2)
  53 #define JAVA_AX_VISIBLE_CHILDREN (-3)
  54 // If the value is >=0, it's an index
  55 
  56 static JNF_STATIC_MEMBER_CACHE(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;");
  57 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;");
  58 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;");
  59 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
  60 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
  61 static JNF_STATIC_MEMBER_CACHE(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
  62 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I");
  63 
  64 static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible");
  65 
  66 static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J");
  67 static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;");
  68 
  69 
  70 static jobject sAccessibilityClass = NULL;
  71 
  72 // sAttributeNamesForRoleCache holds the names of the attributes to which each java
  73 // AccessibleRole responds (see AccessibleRole.java).
  74 // This cache is queried before attempting to access a given attribute for a particular role.
  75 static NSMutableDictionary *sAttributeNamesForRoleCache = nil;
  76 static NSObject *sAttributeNamesLOCK = nil;
  77 
  78 
  79 @interface TabGroupAccessibility : JavaComponentAccessibility {
  80     NSInteger _numTabs;
  81 }
  82 
  83 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext;
  84 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
  85 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
  86 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
  87 
  88 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
  89 - (NSArray *)accessibilityChildrenAttribute;
  90 - (id) accessibilityTabsAttribute;
  91 - (BOOL)accessibilityIsTabsAttributeSettable;
  92 - (NSArray *)accessibilityContentsAttribute;
  93 - (BOOL)accessibilityIsContentsAttributeSettable;
  94 - (id) accessibilityValueAttribute;
  95 
  96 @end
  97 
  98 
  99 @interface TabGroupControlAccessibility : JavaComponentAccessibility {
 100     jobject fTabGroupAxContext;
 101 }
 102 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole;
 103 - (jobject)tabGroup;
 104 - (void)getActionsWithEnv:(JNIEnv *)env;
 105 
 106 - (id)accessibilityValueAttribute;
 107 @end
 108 
 109 
 110 @interface ScrollAreaAccessibility : JavaComponentAccessibility {
 111 
 112 }
 113 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
 114 - (NSArray *)accessibilityContentsAttribute;
 115 - (BOOL)accessibilityIsContentsAttributeSettable;
 116 - (id)accessibilityVerticalScrollBarAttribute;
 117 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable;
 118 - (id)accessibilityHorizontalScrollBarAttribute;
 119 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable;
 120 @end
 121 
 122 
 123 @implementation JavaComponentAccessibility
 124 
 125 - (NSString *)description
 126 {
 127     return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute],
 128         [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]];
 129 }
 130 
 131 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
 132 {
 133     self = [super init];
 134     if (self)
 135     {
 136         fParent = [parent retain];
 137         fView = [view retain];
 138         fJavaRole = [javaRole retain];
 139 
 140         fAccessible = JNFNewGlobalRef(env, accessible);
 141         
 142         jobject jcomponent = [(AWTView *)fView awtComponent:env];
 143         fComponent = JNFNewGlobalRef(env, jcomponent);
 144         (*env)->DeleteLocalRef(env, jcomponent);
 145 
 146         fIndex = index;
 147 
 148         fActions = nil;
 149         fActionsLOCK = [[NSObject alloc] init];
 150     }
 151     return self;
 152 }
 153 
 154 - (void)unregisterFromCocoaAXSystem
 155 {
 156     AWT_ASSERT_APPKIT_THREAD;
 157     static dispatch_once_t initialize_unregisterUniqueId_once;
 158     static void (*unregisterUniqueId)(id);
 159     dispatch_once(&initialize_unregisterUniqueId_once, ^{
 160         void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL);
 161         unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement");
 162     });
 163     if (unregisterUniqueId) unregisterUniqueId(self);
 164 }
 165 
 166 - (void)dealloc
 167 {
 168     [self unregisterFromCocoaAXSystem];
 169     
 170     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 171 
 172     JNFDeleteGlobalRef(env, fAccessible);
 173     fAccessible = NULL;
 174 
 175     JNFDeleteGlobalRef(env, fComponent);
 176     fComponent = NULL;
 177 
 178     [fParent release];
 179     fParent = nil;
 180 
 181     [fNSRole release];
 182     fNSRole = nil;
 183 
 184     [fJavaRole release];
 185     fJavaRole = nil;
 186 
 187     [fView release];
 188     fView = nil;
 189 
 190     [fActions release];
 191     fActions = nil;
 192 
 193     [fActionsLOCK release];
 194     fActionsLOCK = nil;
 195 
 196     [super dealloc];
 197 }
 198 
 199 - (void)postValueChanged
 200 {
 201     AWT_ASSERT_APPKIT_THREAD;
 202     NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification);
 203 }
 204 
 205 - (void)postSelectionChanged
 206 {
 207     AWT_ASSERT_APPKIT_THREAD;
 208     NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification);
 209 }
 210 
 211 - (BOOL)isEqual:(id)anObject
 212 {
 213     if (![anObject isKindOfClass:[self class]]) return NO;
 214     JavaComponentAccessibility *accessibility = (JavaComponentAccessibility *)anObject;
 215 
 216     JNIEnv* env = [ThreadUtilities getJNIEnv];
 217     return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible);
 218 }
 219 
 220 - (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible
 221 {
 222     return (*env)->IsSameObject(env, fAccessible, accessible);
 223 }
 224 
 225 + (void)initialize
 226 {
 227     if (sAttributeNamesForRoleCache == nil) {
 228         sAttributeNamesLOCK = [[NSObject alloc] init];
 229         sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:10];
 230     }
 231 
 232     if (sRoles == nil) {
 233         initializeRoles();
 234     }
 235 
 236     if (sAccessibilityClass == NULL) {
 237         JNF_STATIC_MEMBER_CACHE(jm_getAccessibility, sjc_CAccessibility, "getAccessibility", "([Ljava/lang/String;)Lsun/lwawt/macosx/CAccessibility;");
 238 
 239 #ifdef JAVA_AX_NO_IGNORES
 240         NSArray *ignoredKeys = [NSArray array];
 241 #else
 242         NSArray *ignoredKeys = [sRoles allKeysForObject:JavaAccessibilityIgnore];
 243 #endif
 244         jobjectArray result = NULL;
 245         jsize count = [ignoredKeys count];
 246 
 247         JNIEnv *env = [ThreadUtilities getJNIEnv];
 248 
 249         static JNF_CLASS_CACHE(jc_String, "java/lang/String");
 250         result = JNFNewObjectArray(env, &jc_String, count);
 251         if (!result) {
 252             NSLog(@"In %s, can't create Java array of String objects", __FUNCTION__);
 253             return;
 254         }
 255 
 256         NSInteger i;
 257         for (i = 0; i < count; i++) {
 258             jstring jString = JNFNSToJavaString(env, [ignoredKeys objectAtIndex:i]);
 259             (*env)->SetObjectArrayElement(env, result, i, jString);
 260             (*env)->DeleteLocalRef(env, jString);
 261         }
 262 
 263         sAccessibilityClass = JNFCallStaticObjectMethod(env, jm_getAccessibility, result); // AWT_THREADING Safe (known object)
 264     }
 265 }
 266 
 267 + (void)postFocusChanged:(id)message
 268 {
 269     AWT_ASSERT_APPKIT_THREAD;
 270     NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification);
 271 }
 272 
 273 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
 274     if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
 275         return jaccessible;
 276     }
 277     else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
 278         return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
 279     }
 280     return NULL;
 281 }
 282 
 283 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored
 284 {
 285     jobjectArray jchildrenAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
 286     if (jchildrenAndRoles == NULL) return nil;
 287     
 288     jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles);
 289     NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child
 290 
 291     NSInteger i;
 292     NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly
 293     
 294     for(i = 0; i < arrayLen; i+=2)
 295     {
 296         jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i);
 297         jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1);
 298 
 299         NSString *childJavaRole = nil;
 300         if (jchildJavaRole != NULL) {
 301             childJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jchildJavaRole, sjf_key));
 302         }
 303 
 304         JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView];
 305         
 306         (*env)->DeleteLocalRef(env, jchild);
 307         (*env)->DeleteLocalRef(env, jchildJavaRole);
 308         
 309         [children addObject:child];
 310         childIndex++;
 311     }
 312     (*env)->DeleteLocalRef(env, jchildrenAndRoles);
 313     
 314     return children;
 315 }
 316 
 317 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view
 318 {
 319     jobject jcomponent = [(AWTView *)view awtComponent:env];
 320     jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent);
 321     NSString *javaRole = getJavaRole(env, jaccessible, jcomponent);
 322     (*env)->DeleteLocalRef(env, jcomponent);
 323 
 324     return [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view];
 325 }
 326 
 327 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
 328 {
 329     return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view];
 330 }
 331 
 332 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
 333 {
 334     // try to fetch the jCAX from Java, and return autoreleased
 335     jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env];
 336     if (jCAX == NULL) return nil;
 337     JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr));
 338     if (value != nil) {
 339         (*env)->DeleteLocalRef(env, jCAX);
 340         return [[value retain] autorelease];
 341     }
 342 
 343     // otherwise, create a new instance
 344     JavaComponentAccessibility *newChild = nil;
 345     if ([javaRole isEqualToString:@"pagetablist"]) {
 346         newChild = [TabGroupAccessibility alloc];
 347     } else if ([javaRole isEqualToString:@"scrollpane"]) {
 348         newChild = [ScrollAreaAccessibility alloc];
 349     } else {
 350         NSString *nsRole = [sRoles objectForKey:javaRole];
 351         if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
 352             newChild = [JavaTextAccessibility alloc];
 353         } else {
 354             newChild = [JavaComponentAccessibility alloc];
 355         }
 356     }
 357 
 358     // must init freshly -alloc'd object
 359     [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
 360 
 361     // must hard retain pointer poked into Java object
 362     [newChild retain];
 363     JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
 364     (*env)->DeleteLocalRef(env, jCAX);
 365 
 366     // return autoreleased instance
 367     return [newChild autorelease];
 368 }
 369 
 370 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
 371 {
 372     static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z");
 373 
 374     NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:10];
 375     [attributeNames retain];
 376 
 377     // all elements respond to parent, role, role description, window, topLevelUIElement, help
 378     [attributeNames addObject:NSAccessibilityParentAttribute];
 379     [attributeNames addObject:NSAccessibilityRoleAttribute];
 380     [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute];
 381     [attributeNames addObject:NSAccessibilityHelpAttribute];
 382 
 383     // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window
 384     // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus?
 385     [attributeNames addObject:NSAccessibilityWindowAttribute];
 386     [attributeNames addObject:NSAccessibilityTopLevelUIElementAttribute];
 387 
 388     // set accessible subrole
 389     NSString *javaRole = [self javaRole];
 390     if (javaRole != nil && [javaRole isEqualToString:@"passwordtext"]) {
 391         //cmcnote: should turn this into a constant
 392         [attributeNames addObject:NSAccessibilitySubroleAttribute];
 393     }
 394 
 395     // Get all the other accessibility attributes states we need in one swell foop.
 396     // javaRole isn't pulled in because we need protected access to AccessibleRole.key
 397     jbooleanArray attributeStates = JNFCallStaticObjectMethod(env, jm_getInitialAttributeStates, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 398     if (attributeStates == NULL) return nil;
 399     jboolean *attributeStatesArray = (*env)->GetBooleanArrayElements(env, attributeStates, 0);
 400     if (attributeStatesArray == NULL) {
 401         // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck.
 402         NSLog(@"%s failed calling GetBooleanArrayElements", __FUNCTION__);
 403         return nil;
 404     }
 405 
 406     // if there's a component, it can be enabled and it has a size/position
 407     if (attributeStatesArray[0]) {
 408         [attributeNames addObject:NSAccessibilityEnabledAttribute];
 409         [attributeNames addObject:NSAccessibilitySizeAttribute];
 410         [attributeNames addObject:NSAccessibilityPositionAttribute];
 411     }
 412 
 413     // According to javadoc, a component that is focusable will return true from isFocusTraversable,
 414     // as well as having AccessibleState.FOCUSABLE in it's AccessibleStateSet.
 415     // We use the former heuristic; if the component focus-traversable, add a focused attribute
 416     // See also: accessibilityIsFocusedAttributeSettable
 417     if (attributeStatesArray[1])
 418     {
 419         [attributeNames addObject:NSAccessibilityFocusedAttribute];
 420     }
 421 
 422     // if it's a pagetab / radiobutton, it has a value but no min/max value.
 423     BOOL hasAxValue = attributeStatesArray[2];
 424     if ([javaRole isEqualToString:@"pagetab"] || [javaRole isEqualToString:@"radiobutton"]) {
 425         [attributeNames addObject:NSAccessibilityValueAttribute];
 426     } else {
 427         // if not a pagetab/radio button, and it has a value, it has a min/max/current value.
 428         if (hasAxValue) {
 429             // er, it has a min/max/current value if it's not a button.
 430             // See AppKit/NSButtonCellAccessibility.m
 431             if (![javaRole isEqualToString:@"pushbutton"]) {
 432                 //cmcnote: make this (and "passwordtext") constants instead of magic strings
 433                 [attributeNames addObject:NSAccessibilityMinValueAttribute];
 434                 [attributeNames addObject:NSAccessibilityMaxValueAttribute];
 435                 [attributeNames addObject:NSAccessibilityValueAttribute];
 436             }
 437         }
 438     }
 439 
 440     // does it have an orientation?
 441     if (attributeStatesArray[4]) {
 442         [attributeNames addObject:NSAccessibilityOrientationAttribute];
 443     }
 444 
 445     // name
 446     if (attributeStatesArray[5]) {
 447         [attributeNames addObject:NSAccessibilityTitleAttribute];
 448     }
 449 
 450     // children
 451     if (attributeStatesArray[6]) {
 452         [attributeNames addObject:NSAccessibilityChildrenAttribute];
 453 //        [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute];
 454 //        [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute];
 455                 //According to AXRoles.txt:
 456                 //VisibleChildren: radio group, list, row, table row subrole
 457                 //SelectedChildren: list
 458     }
 459 
 460     // Cleanup
 461     (*env)->ReleaseBooleanArrayElements(env, attributeStates, attributeStatesArray, JNI_ABORT);
 462 
 463     return attributeNames;
 464 }
 465 
 466 - (NSDictionary *)getActions:(JNIEnv *)env
 467 {
 468     @synchronized(fActionsLOCK) {
 469         if (fActions == nil) {
 470             fActions = [[NSMutableDictionary alloc] initWithCapacity:3];
 471             [self getActionsWithEnv:env];
 472         }
 473     }
 474 
 475     return fActions;
 476 }
 477 
 478 - (void)getActionsWithEnv:(JNIEnv *)env
 479 {
 480     static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;");
 481 
 482     // On MacOSX, text doesn't have actions, in java it does.
 483     // cmcnote: NOT TRUE - Editable text has AXShowMenu. Textfields have AXConfirm. Static text has no actions.
 484     jobject axAction = JNFCallStaticObjectMethod(env, jm_getAccessibleAction, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 485     if (axAction != NULL) {
 486         //+++gdb NOTE: In MacOSX, there is just a single Action, not multiple. In java,
 487         //  the first one seems to be the most basic, so this will be used.
 488         // cmcnote: NOT TRUE - Sometimes there are multiple actions, eg sliders have AXDecrement AND AXIncrement (radr://3893192)
 489         JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:0 withComponent:fComponent];
 490         [fActions setObject:action forKey:[self isMenu] ? NSAccessibilityPickAction : NSAccessibilityPressAction];
 491         [action release];
 492     }
 493 }
 494 
 495 - (jobject)axContextWithEnv:(JNIEnv *)env
 496 {
 497     return getAxContext(env, fAccessible, fComponent);
 498 }
 499 
 500 - (id)parent
 501 {
 502     static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
 503 
 504     if(fParent == nil) {
 505         JNIEnv* env = [ThreadUtilities getJNIEnv];
 506 
 507         jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent);
 508 
 509         if (jparent == NULL) {
 510             fParent = fView;
 511         } else {
 512             fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:fView];
 513             (*env)->DeleteLocalRef(env, jparent);
 514             if (fParent == nil) {
 515                 fParent = fView;
 516             }
 517         }
 518         [fParent retain];
 519     }
 520     return fParent;
 521 }
 522 
 523 - (NSView *)view
 524 {
 525     return fView;
 526 }
 527 
 528 - (NSWindow *)window
 529 {
 530     return [[self view] window];
 531 }
 532 
 533 - (NSString *)javaRole
 534 {
 535     if(fJavaRole == nil) {
 536         JNIEnv* env = [ThreadUtilities getJNIEnv];
 537         fJavaRole = getJavaRole(env, fAccessible, fComponent);
 538         [fJavaRole retain];
 539     }
 540     return fJavaRole;
 541 }
 542 
 543 - (BOOL)isMenu
 544 {
 545     id role = [self accessibilityRoleAttribute];
 546     return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole];
 547 }
 548 
 549 - (BOOL)isSelected:(JNIEnv *)env
 550 {
 551     if (fIndex == -1) {
 552         return NO;
 553     }
 554 
 555     return isChildSelected(env, ((JavaComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent);
 556 }
 557 
 558 - (BOOL)isVisible:(JNIEnv *)env
 559 {
 560     if (fIndex == -1) {
 561         return NO;
 562     }
 563 
 564     jobject axContext = [self axContextWithEnv:env];
 565     BOOL showing = isShowing(env, axContext, fComponent);
 566     (*env)->DeleteLocalRef(env, axContext);
 567     return showing;
 568 }
 569 
 570 // the array of names for each role is cached in the sAttributeNamesForRoleCache
 571 - (NSArray *)accessibilityAttributeNames
 572 {
 573     JNIEnv* env = [ThreadUtilities getJNIEnv];
 574 
 575     @synchronized(sAttributeNamesLOCK) {
 576         NSString *javaRole = [self javaRole];
 577         NSArray *names = (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole];
 578         if (names != nil) return names;
 579 
 580         names = [self initializeAttributeNamesWithEnv:env];
 581         if (names != nil) {
 582 #ifdef JAVA_AX_DEBUG
 583             NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names);
 584 #endif
 585             [sAttributeNamesForRoleCache setObject:names forKey:javaRole];
 586             return names;
 587         }
 588     }
 589 
 590 #ifdef JAVA_AX_DEBUG
 591     NSLog(@"Warning in %s: could not find attribute names for role: %@", __FUNCTION__, [self javaRole]);
 592 #endif
 593 
 594     return nil;
 595 }
 596 
 597 // -- accessibility attributes --
 598 
 599 - (BOOL)accessibilityShouldUseUniqueId {
 600     return YES;
 601 }
 602 
 603 - (BOOL)accessibilitySupportsOverriddenAttributes {
 604     return YES;
 605 }
 606 
 607 
 608 // generic getters & setters
 609 // cmcnote: it would make more sense if these generic getters/setters were in JavaAccessibilityUtilities
 610 - (id)accessibilityAttributeValue:(NSString *)attribute
 611 {
 612     AWT_ASSERT_APPKIT_THREAD;
 613 
 614     // turns attribute "NSAccessibilityEnabledAttribute" into getter "accessibilityEnabledAttribute",
 615     // calls getter on self
 616     return JavaAccessibilityAttributeValue(self, attribute);
 617 }
 618 
 619 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute
 620 {
 621     AWT_ASSERT_APPKIT_THREAD;
 622 
 623     // turns attribute "NSAccessibilityParentAttribute" into selector "accessibilityIsParentAttributeSettable",
 624     // calls selector on self
 625     return JavaAccessibilityIsAttributeSettable(self, attribute);
 626 }
 627 
 628 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
 629 {
 630     AWT_ASSERT_APPKIT_THREAD;
 631 
 632     if ([self accessibilityIsAttributeSettable:attribute]) {
 633         // turns attribute "NSAccessibilityFocusAttribute" into setter "accessibilitySetFocusAttribute",
 634         // calls setter on self
 635         JavaAccessibilitySetAttributeValue(self, attribute, value);
 636     }
 637 }
 638 
 639 
 640 // specific attributes, in alphabetical order a la
 641 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
 642 
 643 // Elements that current element contains (NSArray)
 644 - (NSArray *)accessibilityChildrenAttribute
 645 {
 646     JNIEnv* env = [ThreadUtilities getJNIEnv];
 647     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO];
 648 
 649     NSArray *value = nil;
 650     if ([children count] > 0) {
 651         value = children;
 652     }
 653 
 654     return value;
 655 }
 656 - (BOOL)accessibilityIsChildrenAttributeSettable
 657 {
 658     return NO;
 659 }
 660 
 661 - (NSUInteger)accessibilityIndexOfChild:(id)child
 662 {
 663     // Only special-casing for Lists, for now. This allows lists to be accessible, fixing radr://3856139 "JLists are broken".
 664     // Will probably want to special-case for Tables when we implement them (radr://3096643 "Accessibility: Table").
 665     // In AppKit, NSMatrixAccessibility (which uses NSAccessibilityListRole), NSTableRowAccessibility, and NSTableViewAccessibility are the
 666     // only ones that override the default implementation in NSAccessibility
 667     if (![[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityListRole]) {
 668         return [super accessibilityIndexOfChild:child];
 669     }
 670 
 671     return JNFCallStaticIntMethod([ThreadUtilities getJNIEnv], sjm_getAccessibleIndexInParent, ((JavaComponentAccessibility *)child)->fAccessible, ((JavaComponentAccessibility *)child)->fComponent);
 672 }
 673 
 674 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
 675 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
 676     if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
 677         // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
 678         NSArray *child = [JavaComponentAccessibility childrenOfParent:self withEnv:[ThreadUtilities getJNIEnv] withChildrenCode:(NSInteger)index allowIgnored:NO];
 679         if ([child count] > 0) {
 680             return child;
 681         }
 682     }
 683     return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
 684 }
 685 
 686 // Flag indicating enabled state of element (NSNumber)
 687 - (NSNumber *)accessibilityEnabledAttribute
 688 {
 689     static JNF_STATIC_MEMBER_CACHE(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
 690 
 691     JNIEnv* env = [ThreadUtilities getJNIEnv];
 692     NSNumber *value = [NSNumber numberWithBool:JNFCallStaticBooleanMethod(env, jm_isEnabled, fAccessible, fComponent)]; // AWT_THREADING Safe (AWTRunLoop)
 693     if (value == nil) {
 694         NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self);
 695     }
 696     return value;
 697 }
 698 
 699 - (BOOL)accessibilityIsEnabledAttributeSettable
 700 {
 701     return NO;
 702 }
 703 
 704 // Flag indicating presence of keyboard focus (NSNumber)
 705 - (NSNumber *)accessibilityFocusedAttribute
 706 {
 707     if ([self accessibilityIsFocusedAttributeSettable]) {
 708         return [NSNumber numberWithBool:[self isEqual:[NSApp accessibilityFocusedUIElement]]];
 709     }
 710     return [NSNumber numberWithBool:NO];
 711 }
 712 
 713 - (BOOL)accessibilityIsFocusedAttributeSettable
 714 {
 715     JNIEnv* env = [ThreadUtilities getJNIEnv];
 716     // According to javadoc, a component that is focusable will return true from isFocusTraversable,
 717     // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet.
 718     // We use the former heuristic; if the component focus-traversable, add a focused attribute
 719     // See also initializeAttributeNamesWithEnv:
 720     if (JNFCallStaticBooleanMethod(env, sjm_isFocusTraversable, fAccessible, fComponent)) { // AWT_THREADING Safe (AWTRunLoop)
 721         return YES;
 722     }
 723 
 724     return NO;
 725 }
 726 
 727 - (void)accessibilitySetFocusedAttribute:(id)value
 728 {
 729     static JNF_STATIC_MEMBER_CACHE(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V");
 730 
 731     if ([(NSNumber*)value boolValue])
 732     {
 733         JNIEnv* env = [ThreadUtilities getJNIEnv];
 734         JNFCallStaticVoidMethod(env, jm_requestFocus, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 735     }
 736 }
 737 
 738 // Instance description, such as a help tag string (NSString)
 739 - (NSString *)accessibilityHelpAttribute
 740 {
 741     JNIEnv* env = [ThreadUtilities getJNIEnv];
 742 
 743     jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 744     return JNFJavaToNSString(env, val);
 745 }
 746 
 747 - (BOOL)accessibilityIsHelpAttributeSettable
 748 {
 749     return NO;
 750 }
 751 
 752 // Element's maximum value (id)
 753 - (id)accessibilityMaxValueAttribute
 754 {
 755     static JNF_STATIC_MEMBER_CACHE(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
 756 
 757     JNIEnv* env = [ThreadUtilities getJNIEnv];
 758 
 759     jobject axValue = JNFCallStaticObjectMethod(env, jm_getMaximumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 760     return JNFJavaToNSNumber(env, axValue);
 761 }
 762 
 763 - (BOOL)accessibilityIsMaxValueAttributeSettable
 764 {
 765     return NO;
 766 }
 767 
 768 // Element's minimum value (id)
 769 - (id)accessibilityMinValueAttribute
 770 {
 771     static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
 772 
 773     JNIEnv* env = [ThreadUtilities getJNIEnv];
 774 
 775     jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 776     return JNFJavaToNSNumber(env, axValue);
 777 }
 778 
 779 - (BOOL)accessibilityIsMinValueAttributeSettable
 780 {
 781     return NO;
 782 }
 783 
 784 - (id)accessibilityOrientationAttribute
 785 {
 786     JNIEnv* env = [ThreadUtilities getJNIEnv];
 787     jobject axContext = [self axContextWithEnv:env];
 788 
 789     // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz
 790     if (isVertical(env, axContext, fComponent)) {
 791         (*env)->DeleteLocalRef(env, axContext);
 792         return NSAccessibilityVerticalOrientationValue;
 793     }
 794 
 795     if (isHorizontal(env, axContext, fComponent)) {
 796         (*env)->DeleteLocalRef(env, axContext);
 797         return NSAccessibilityHorizontalOrientationValue;
 798     }
 799 
 800     (*env)->DeleteLocalRef(env, axContext);
 801     return nil;
 802 }
 803 
 804 - (BOOL)accessibilityIsOrientationAttributeSettable
 805 {
 806     return NO;
 807 }
 808 
 809 // Element containing current element (id)
 810 - (id)accessibilityParentAttribute
 811 {
 812     return NSAccessibilityUnignoredAncestor([self parent]);
 813 }
 814 
 815 - (BOOL)accessibilityIsParentAttributeSettable
 816 {
 817     return NO;
 818 }
 819 
 820 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue)
 821 - (NSValue *)accessibilityPositionAttribute
 822 {
 823     JNIEnv* env = [ThreadUtilities getJNIEnv];
 824     jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 825 
 826     // NSAccessibility wants the bottom left point of the object in
 827     // bottom left based screen coords
 828 
 829     // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent.
 830     NSSize size = getAxComponentSize(env, axComponent, fComponent);
 831     NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent);
 832     (*env)->DeleteLocalRef(env, axComponent);
 833 
 834     point.y += size.height;
 835 
 836     // Now make it into Cocoa screen coords.
 837     point.y = [[[[self view] window] screen] frame].size.height - point.y;
 838 
 839     return [NSValue valueWithPoint:point];
 840 }
 841 
 842 - (BOOL)accessibilityIsPositionAttributeSettable
 843 {
 844     // In AppKit, position is only settable for a window (NSAccessibilityWindowRole). Our windows are taken care of natively, so we don't need to deal with this here
 845     // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869
 846     return NO;
 847 }
 848 
 849 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table
 850 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
 851 - (NSString *)accessibilityRoleAttribute
 852 {
 853     if (fNSRole == nil) {
 854         NSString *javaRole = [self javaRole];
 855         fNSRole = [sRoles objectForKey:javaRole];
 856         if (fNSRole == nil) {
 857             // this component has assigned itself a custom AccessibleRole not in the sRoles array
 858             fNSRole = javaRole;
 859         }
 860         [fNSRole retain];
 861     }
 862     return fNSRole;
 863 }
 864 - (BOOL)accessibilityIsRoleAttributeSettable
 865 {
 866     return NO;
 867 }
 868 
 869 // Localized, user-readable description of role, such as radio button (NSString)
 870 - (NSString *)accessibilityRoleDescriptionAttribute
 871 {
 872     // first ask AppKit for its accessible role description for a given AXRole
 873     NSString *value = NSAccessibilityRoleDescription([self accessibilityRoleAttribute], nil);
 874 
 875     if (value == nil) {
 876         // query java if necessary
 877         static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
 878 
 879         JNIEnv* env = [ThreadUtilities getJNIEnv];
 880 
 881         jobject axRole = JNFCallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent);
 882         if(axRole != NULL) {
 883             value = JNFJavaToNSString(env, axRole);
 884         } else {
 885             value = @"unknown";
 886         }
 887     }
 888 
 889     return value;
 890 }
 891 
 892 - (BOOL)accessibilityIsRoleDescriptionAttributeSettable
 893 {
 894     return NO;
 895 }
 896 
 897 // Currently selected children (NSArray)
 898 - (NSArray *)accessibilitySelectedChildrenAttribute
 899 {
 900     JNIEnv* env = [ThreadUtilities getJNIEnv];
 901     NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO];
 902     if ([selectedChildren count] > 0) {
 903         return selectedChildren;
 904     }
 905 
 906     return nil;
 907 }
 908 
 909 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable
 910 {
 911     return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also
 912 }
 913 
 914 // Element size (NSValue)
 915 - (NSValue *)accessibilitySizeAttribute {
 916     JNIEnv* env = [ThreadUtilities getJNIEnv];
 917     jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 918     NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)];
 919     (*env)->DeleteLocalRef(env, axComponent);
 920     return size;
 921 }
 922 
 923 - (BOOL)accessibilityIsSizeAttributeSettable
 924 {
 925     // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here
 926     // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet
 927     return NO;
 928 }
 929 
 930 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at
 931 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
 932 - (NSString *)accessibilitySubroleAttribute
 933 {
 934     NSString *value = nil;
 935     if ([[self javaRole] isEqualToString:@"passwordtext"])
 936     {
 937         value = NSAccessibilitySecureTextFieldSubrole;
 938     }
 939     /*
 940     // other subroles. TableRow and OutlineRow may be relevant to us
 941      NSAccessibilityCloseButtonSubrole // no, heavyweight window takes care of this
 942      NSAccessibilityMinimizeButtonSubrole // "
 943      NSAccessibilityOutlineRowSubrole    // maybe?
 944      NSAccessibilitySecureTextFieldSubrole // currently used
 945      NSAccessibilityTableRowSubrole        // maybe?
 946      NSAccessibilityToolbarButtonSubrole // maybe?
 947      NSAccessibilityUnknownSubrole
 948      NSAccessibilityZoomButtonSubrole    // no, heavyweight window takes care of this
 949      NSAccessibilityStandardWindowSubrole// no, heavyweight window takes care of this
 950      NSAccessibilityDialogSubrole        // maybe?
 951      NSAccessibilitySystemDialogSubrole    // no
 952      NSAccessibilityFloatingWindowSubrole // in 1.5 if we implement these, heavyweight will take care of them anyway
 953      NSAccessibilitySystemFloatingWindowSubrole
 954      NSAccessibilityIncrementArrowSubrole  // no
 955      NSAccessibilityDecrementArrowSubrole  // no
 956      NSAccessibilityIncrementPageSubrole   // no
 957      NSAccessibilityDecrementPageSubrole   // no
 958      NSAccessibilitySearchFieldSubrole    //no
 959      */
 960     return value;
 961 }
 962 
 963 - (BOOL)accessibilityIsSubroleAttributeSettable
 964 {
 965     return NO;
 966 }
 967 
 968 // Title of element, such as button text (NSString)
 969 - (NSString *)accessibilityTitleAttribute
 970 {
 971     // Return empty string for labels, since their value and tile end up being the same thing and this leads to repeated text.
 972     if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) {
 973         return @"";
 974     }
 975 
 976     JNIEnv* env = [ThreadUtilities getJNIEnv];
 977 
 978     jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 979     return JNFJavaToNSString(env, val);
 980 }
 981 
 982 - (BOOL)accessibilityIsTitleAttributeSettable
 983 {
 984     return NO;
 985 }
 986 
 987 - (NSWindow *)accessibilityTopLevelUIElementAttribute
 988 {
 989     return [self window];
 990 }
 991 
 992 - (BOOL)accessibilityIsTopLevelUIElementAttributeSettable
 993 {
 994     return NO;
 995 }
 996 
 997 // Element's value (id)
 998 // note that the appKit meaning of "accessibilityValue" is different from the java
 999 // meaning of "accessibleValue", which is specific to numerical values
1000 // (http://java.sun.com/j2se/1.3/docs/api/javax/accessibility/AccessibleValue.html#setCurrentAccessibleValue(java.lang.Number))
1001 - (id)accessibilityValueAttribute
1002 {
1003     static JNF_STATIC_MEMBER_CACHE(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;");
1004 
1005     JNIEnv* env = [ThreadUtilities getJNIEnv];
1006 
1007     // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value
1008     // a text value is taken care of in JavaTextAccessibility
1009 
1010     // cmcnote should coalesce these calls into one java call
1011     jobject axValue = JNFCallStaticObjectMethod(env, sjm_getAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1012     return JNFJavaToNSNumber(env, JNFCallStaticObjectMethod(env, jm_getCurrentAccessibleValue, axValue, fComponent)); // AWT_THREADING Safe (AWTRunLoop)
1013 }
1014 
1015 - (BOOL)accessibilityIsValueAttributeSettable
1016 {
1017     // according ot AppKit sources, in general the value attribute is not settable, except in the cases
1018     // of an NSScroller, an NSSplitView, and text that's both enabled & editable
1019     BOOL isSettable = NO;
1020     NSString *role = [self accessibilityRoleAttribute];
1021 
1022     if ([role isEqualToString:NSAccessibilityScrollBarRole] || // according to NSScrollerAccessibility
1023         [role isEqualToString:NSAccessibilitySplitGroupRole] ) // according to NSSplitViewAccessibility
1024     {
1025         isSettable = YES;
1026     }
1027     return isSettable;
1028 }
1029 
1030 - (void)accessibilitySetValueAttribute:(id)value
1031 {
1032 #ifdef JAVA_AX_DEBUG
1033     NSLog(@"Not yet implemented: %s\n", __FUNCTION__); // radr://3954018
1034 #endif
1035 }
1036 
1037 
1038 // Child elements that are visible (NSArray)
1039 - (NSArray *)accessibilityVisibleChildrenAttribute
1040 {
1041     JNIEnv *env = [ThreadUtilities getJNIEnv];
1042     NSArray *visibleChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO];
1043     if ([visibleChildren count] <= 0) return nil;
1044     return visibleChildren;
1045 }
1046 
1047 - (BOOL)accessibilityIsVisibleChildrenAttributeSettable
1048 {
1049     return NO;
1050 }
1051 
1052 // Window containing current element (id)
1053 - (id)accessibilityWindowAttribute
1054 {
1055     return [self window];
1056 }
1057 
1058 - (BOOL)accessibilityIsWindowAttributeSettable
1059 {
1060     return NO;
1061 }
1062 
1063 
1064 // -- accessibility actions --
1065 - (NSArray *)accessibilityActionNames
1066 {
1067     JNIEnv *env = [ThreadUtilities getJNIEnv];
1068     return [[self getActions:env] allKeys];
1069 }
1070 
1071 - (NSString *)accessibilityActionDescription:(NSString *)action
1072 {
1073     AWT_ASSERT_APPKIT_THREAD;
1074 
1075     JNIEnv *env = [ThreadUtilities getJNIEnv];
1076     return [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] getDescription];
1077 }
1078 
1079 - (void)accessibilityPerformAction:(NSString *)action
1080 {
1081     AWT_ASSERT_APPKIT_THREAD;
1082 
1083     JNIEnv *env = [ThreadUtilities getJNIEnv];
1084     [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] perform];
1085 }
1086 
1087 
1088 // -- misc accessibility --
1089 - (BOOL)accessibilityIsIgnored
1090 {
1091 #ifdef JAVA_AX_NO_IGNORES
1092     return NO;
1093 #else
1094     return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore];
1095 #endif /* JAVA_AX_NO_IGNORES */
1096 }
1097 
1098 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
1099 {
1100     static JNF_CLASS_CACHE(jc_Container, "java/awt/Container");
1101     static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;");
1102 
1103     // Make it into java screen coords
1104     point.y = [[[[self view] window] screen] frame].size.height - point.y;
1105 
1106     jobject jparent = fComponent;
1107 
1108     id value = nil;
1109     if (JNFIsInstanceOf(env, jparent, &jc_Container)) {
1110         jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop)
1111         value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView];
1112         (*env)->DeleteLocalRef(env, jaccessible);
1113     }
1114 
1115     if (value == nil) {
1116         value = self;
1117     }
1118 
1119     if ([value accessibilityIsIgnored]) {
1120         value = NSAccessibilityUnignoredAncestor(value);
1121     }
1122 
1123 #ifdef JAVA_AX_DEBUG
1124     NSLog(@"%s: %@", __FUNCTION__, value);
1125 #endif
1126     return value;
1127 }
1128 
1129 - (id)accessibilityFocusedUIElement
1130 {
1131     static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
1132 
1133     JNIEnv *env = [ThreadUtilities getJNIEnv];
1134     id value = nil;
1135 
1136     NSWindow* hostWindow = [[self->fView window] retain];
1137     jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1138     [hostWindow release];
1139     
1140     if (focused != NULL) {
1141         if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) {
1142             value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView];
1143         }
1144         (*env)->DeleteLocalRef(env, focused);
1145     }
1146 
1147     if (value == nil) {
1148         value = self;
1149     }
1150 #ifdef JAVA_AX_DEBUG
1151     NSLog(@"%s: %@", __FUNCTION__, value);
1152 #endif
1153     return value;
1154 }
1155 
1156 @end
1157 
1158 /*
1159  * Class:     sun_lwawt_macosx_CAccessibility
1160  * Method:    focusChanged
1161  * Signature: ()V
1162  */
1163 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged
1164 (JNIEnv *env, jobject jthis)
1165 {
1166 
1167 JNF_COCOA_ENTER(env);
1168     [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO];
1169 JNF_COCOA_EXIT(env);
1170 }
1171 
1172 
1173 
1174 /*
1175  * Class:     sun_lwawt_macosx_CAccessible
1176  * Method:    valueChanged
1177  * Signature: (I)V
1178  */
1179 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged
1180 (JNIEnv *env, jclass jklass, jlong element)
1181 {
1182 JNF_COCOA_ENTER(env);
1183     [ThreadUtilities performOnMainThread:@selector(postValueChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1184 JNF_COCOA_EXIT(env);
1185 }
1186 
1187 /*
1188  * Class:     sun_lwawt_macosx_CAccessible
1189  * Method:    selectionChanged
1190  * Signature: (I)V
1191  */
1192 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged
1193 (JNIEnv *env, jclass jklass, jlong element)
1194 {
1195 JNF_COCOA_ENTER(env);
1196     [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1197 JNF_COCOA_EXIT(env);
1198 }
1199 
1200 
1201 /*
1202  * Class:     sun_lwawt_macosx_CAccessible
1203  * Method:    unregisterFromCocoaAXSystem
1204  * Signature: (I)V
1205  */
1206 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem
1207 (JNIEnv *env, jclass jklass, jlong element)
1208 {
1209 JNF_COCOA_ENTER(env);
1210     [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1211 JNF_COCOA_EXIT(env);
1212 }
1213 
1214 @implementation TabGroupAccessibility
1215 
1216 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
1217 {
1218     self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1219     if (self) {
1220         _numTabs = -1; //flag for uninitialized numTabs
1221     }
1222     return self;
1223 }
1224 
1225 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1226 {
1227     NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1228 
1229     [names addObject:NSAccessibilityTabsAttribute];
1230     [names addObject:NSAccessibilityContentsAttribute];
1231     [names addObject:NSAccessibilityValueAttribute];
1232 
1233     return names;
1234 }
1235 
1236 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext
1237 {
1238     NSArray *tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1239 
1240     // Looking at the JTabbedPane sources, there is always one AccessibleSelection.
1241     jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent);
1242     if (selAccessible == NULL) return nil;
1243 
1244     // Go through the tabs and find selAccessible
1245     _numTabs = [tabs count];
1246     JavaComponentAccessibility *aTab;
1247     NSInteger i;
1248     for (i = 0; i < _numTabs; i++) {
1249         aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i];
1250         if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) {
1251             return aTab;
1252         }
1253     }
1254 
1255     return nil;
1256 }
1257 
1258 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1259 {
1260     jobjectArray jtabsAndRoles = JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
1261     if(jtabsAndRoles == NULL) return nil;
1262 
1263     jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles);
1264     if (arrayLen == 0) {
1265         (*env)->DeleteLocalRef(env, jtabsAndRoles);
1266         return nil;
1267     }
1268     NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)];
1269 
1270     // all of the tabs have the same role, so we can just find out what that is here and use it for all the tabs
1271     jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1.
1272     if (jtabJavaRole == NULL) {
1273         (*env)->DeleteLocalRef(env, jtabsAndRoles);
1274         return nil;
1275     }
1276     NSString *tabJavaRole = JNFJavaToNSString(env, JNFGetObjectField(env, jtabJavaRole, sjf_key));
1277 
1278     NSInteger i;
1279     NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly
1280     for(i = 0; i < arrayLen; i+=2) {
1281         jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i);
1282         JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease];
1283         [tabs addObject:tab];
1284         tabIndex++;
1285     }
1286     (*env)->DeleteLocalRef(env, jtabsAndRoles);
1287     return tabs;
1288 }
1289 
1290 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1291 {
1292     // Contents are the children of the selected tab.
1293     id currentTab = [self currentTabWithEnv:env withAxContext:axContext];
1294     if (currentTab == nil) return nil;
1295 
1296     NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored];
1297     if ([contents count] <= 0) return nil;
1298     return contents;
1299 }
1300 
1301 - (id) accessibilityTabsAttribute
1302 {
1303     JNIEnv *env = [ThreadUtilities getJNIEnv];
1304     jobject axContext = [self axContextWithEnv:env];
1305     id tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1306     (*env)->DeleteLocalRef(env, axContext);
1307     return tabs;
1308 }
1309 
1310 - (BOOL)accessibilityIsTabsAttributeSettable
1311 {
1312     return NO; //cmcnote: not sure.
1313 }
1314 
1315 - (NSInteger)numTabs
1316 {
1317     if (_numTabs == -1) {
1318         _numTabs = [[self accessibilityTabsAttribute] count];
1319     }
1320     return _numTabs;
1321 }
1322 
1323 - (NSArray *) accessibilityContentsAttribute
1324 {
1325     JNIEnv *env = [ThreadUtilities getJNIEnv];
1326     jobject axContext = [self axContextWithEnv:env];
1327     NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1328     (*env)->DeleteLocalRef(env, axContext);
1329     return cont;
1330 }
1331 
1332 - (BOOL)accessibilityIsContentsAttributeSettable
1333 {
1334     return NO;
1335 }
1336 
1337 // axValue is the currently selected tab
1338 -(id) accessibilityValueAttribute
1339 {
1340     JNIEnv *env = [ThreadUtilities getJNIEnv];
1341     jobject axContext = [self axContextWithEnv:env];
1342     id val = [self currentTabWithEnv:env withAxContext:axContext];
1343     (*env)->DeleteLocalRef(env, axContext);
1344     return val;
1345 }
1346 
1347 - (BOOL)accessibilityIsValueAttributeSettable
1348 {
1349     return YES;
1350 }
1351 
1352 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate.
1353 {
1354     // set the current tab
1355     NSNumber *number = (NSNumber *)value;
1356     if (![number boolValue]) return;
1357 
1358     JNIEnv *env = [ThreadUtilities getJNIEnv];
1359     jobject axContext = [self axContextWithEnv:env];
1360     setAxContextSelection(env, axContext, fIndex, fComponent);
1361     (*env)->DeleteLocalRef(env, axContext);
1362 }
1363 
1364 - (NSArray *)accessibilityChildrenAttribute
1365 {
1366     //children = AXTabs + AXContents
1367     NSArray *tabs = [self accessibilityTabsAttribute];
1368     NSArray *contents = [self accessibilityContentsAttribute];
1369 
1370     NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]];
1371     [children addObjectsFromArray:tabs];
1372     [children addObjectsFromArray:contents];
1373 
1374     return (NSArray *)children;
1375 }
1376 
1377 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
1378 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since
1379 // children of tabs are AXTabs + AXContents
1380 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
1381     NSArray *result = nil;
1382     if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1383         // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
1384         JNIEnv *env = [ThreadUtilities getJNIEnv];
1385         jobject axContext = [self axContextWithEnv:env];
1386 
1387         //children = AXTabs + AXContents
1388         NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs
1389         if ([children count] > 0) {
1390             result = children;
1391         } else {
1392             children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO];
1393             if ([children count] > 0) {
1394                 result = children;
1395             }
1396         }
1397         (*env)->DeleteLocalRef(env, axContext);
1398     } else {
1399         result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
1400     }
1401     return result;
1402 }
1403 
1404 @end
1405 
1406 
1407 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
1408 
1409 @implementation TabGroupControlAccessibility
1410 
1411 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
1412 {
1413     self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1414     if (self) {
1415         if (tabGroup != NULL) {
1416             fTabGroupAxContext = JNFNewGlobalRef(env, tabGroup);
1417         } else {
1418             fTabGroupAxContext = NULL;
1419         }
1420     }
1421     return self;
1422 }
1423 
1424 - (void)dealloc
1425 {
1426     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
1427 
1428     if (fTabGroupAxContext != NULL) {
1429         JNFDeleteGlobalRef(env, fTabGroupAxContext);
1430         fTabGroupAxContext = NULL;
1431     }
1432 
1433     [super dealloc];
1434 }
1435 
1436 - (id)accessibilityValueAttribute
1437 {
1438     JNIEnv *env = [ThreadUtilities getJNIEnv];
1439     jobject axContext = [self axContextWithEnv:env];
1440 
1441     // Returns the current selection of the page tab list
1442     id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, getAxContextSelection(env, [self tabGroup], fIndex, fComponent), fComponent)];
1443     (*env)->DeleteLocalRef(env, axContext);
1444     return val;
1445 }
1446 
1447 - (void)getActionsWithEnv:(JNIEnv *)env
1448 {
1449     TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent];
1450     [fActions setObject:action forKey:NSAccessibilityPressAction];
1451     [action release];
1452 }
1453 
1454 - (jobject)tabGroup
1455 {
1456     if (fTabGroupAxContext == NULL) {
1457         JNIEnv* env = [ThreadUtilities getJNIEnv];
1458         jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env];
1459         fTabGroupAxContext = JNFNewGlobalRef(env, tabGroupAxContext);
1460         (*env)->DeleteLocalRef(env, tabGroupAxContext);
1461     }
1462     return fTabGroupAxContext;
1463 }
1464 
1465 @end
1466 
1467 
1468 @implementation ScrollAreaAccessibility
1469 
1470 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1471 {
1472     NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1473 
1474     [names addObject:NSAccessibilityHorizontalScrollBarAttribute];
1475     [names addObject:NSAccessibilityVerticalScrollBarAttribute];
1476     [names addObject:NSAccessibilityContentsAttribute];
1477 
1478     return names;
1479 }
1480 
1481 - (id)accessibilityHorizontalScrollBarAttribute
1482 {
1483     JNIEnv *env = [ThreadUtilities getJNIEnv];
1484 
1485     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1486     if ([children count] <= 0) return nil;
1487 
1488     // The scroll bars are in the children.
1489     JavaComponentAccessibility *aElement;
1490     NSEnumerator *enumerator = [children objectEnumerator];
1491     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1492         if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1493             jobject elementAxContext = [aElement axContextWithEnv:env];
1494             if (isHorizontal(env, elementAxContext, fComponent)) {
1495                 (*env)->DeleteLocalRef(env, elementAxContext);
1496                 return aElement;
1497             }
1498             (*env)->DeleteLocalRef(env, elementAxContext);
1499         }
1500     }
1501 
1502     return nil;
1503 }
1504 
1505 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable
1506 {
1507     return NO;
1508 }
1509 
1510 - (id)accessibilityVerticalScrollBarAttribute
1511 {
1512     JNIEnv *env = [ThreadUtilities getJNIEnv];
1513 
1514     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1515     if ([children count] <= 0) return nil;
1516 
1517     // The scroll bars are in the children.
1518     NSEnumerator *enumerator = [children objectEnumerator];
1519     JavaComponentAccessibility *aElement;
1520     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1521         if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1522             jobject elementAxContext = [aElement axContextWithEnv:env];
1523             if (isVertical(env, elementAxContext, fComponent)) {
1524                 (*env)->DeleteLocalRef(env, elementAxContext);
1525                 return aElement;
1526             }
1527             (*env)->DeleteLocalRef(env, elementAxContext);
1528         }
1529     }
1530 
1531     return nil;
1532 }
1533 
1534 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable
1535 {
1536     return NO;
1537 }
1538 
1539 - (NSArray *)accessibilityContentsAttribute
1540 {
1541     JNIEnv *env = [ThreadUtilities getJNIEnv];
1542     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1543 
1544     if ([children count] <= 0) return nil;
1545     NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]];
1546 
1547     // The scroll bars are in the children. children less the scroll bars is the contents
1548     NSEnumerator *enumerator = [children objectEnumerator];
1549     JavaComponentAccessibility *aElement;
1550     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1551         if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1552             // no scroll bars in contents
1553             [(NSMutableArray *)contents addObject:aElement];
1554         }
1555     }
1556 
1557     return contents;
1558 }
1559 
1560 - (BOOL)accessibilityIsContentsAttributeSettable
1561 {
1562     return NO;
1563 }
1564 
1565 @end
1566 
1567 /*
1568  * Returns Object.equals for the two items
1569  * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock
1570  * and try to pass a component so the event happens on the correct thread.
1571  */
1572 static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object");
1573 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component)
1574 {
1575     static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z");
1576 
1577     if ((a == NULL) && (b == NULL)) return YES;
1578     if ((a == NULL) || (b == NULL)) return NO;
1579 
1580     if (pthread_main_np() != 0) {
1581         // If we are on the AppKit thread
1582         static JNF_CLASS_CACHE(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
1583         static JNF_STATIC_MEMBER_CACHE(jm_doEquals, sjc_LWCToolkit, "doEquals", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z");
1584         return JNFCallStaticBooleanMethod(env, jm_doEquals, a, b, component); // AWT_THREADING Safe (AWTRunLoopMode)
1585     }
1586 
1587     return JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit)
1588 }