1 /*
   2  * Copyright (c) 2011, 2018, 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 // <https://docs.oracle.com/javase/8/docs/technotes/guides/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(jm_getTableInfo, sjc_CAccessibility, "getTableInfo", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)I");
  58 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;");
  59 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;");
  60 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
  61 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
  62 static JNF_STATIC_MEMBER_CACHE(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
  63 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I");
  64 
  65 static JNF_CLASS_CACHE(sjc_CAccessible, "sun/lwawt/macosx/CAccessible");
  66 
  67 static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J");
  68 static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;");
  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 @interface TabGroupAccessibility : JavaComponentAccessibility {
  79     NSInteger _numTabs;
  80 }
  81 
  82 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext;
  83 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
  84 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored;
  85 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
  86 
  87 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
  88 - (NSArray *)accessibilityChildrenAttribute;
  89 - (id) accessibilityTabsAttribute;
  90 - (BOOL)accessibilityIsTabsAttributeSettable;
  91 - (NSArray *)accessibilityContentsAttribute;
  92 - (BOOL)accessibilityIsContentsAttributeSettable;
  93 - (id) accessibilityValueAttribute;
  94 
  95 @end
  96 
  97 
  98 @interface TabGroupControlAccessibility : JavaComponentAccessibility {
  99     jobject fTabGroupAxContext;
 100 }
 101 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole;
 102 - (jobject)tabGroup;
 103 - (void)getActionsWithEnv:(JNIEnv *)env;
 104 
 105 - (id)accessibilityValueAttribute;
 106 @end
 107 
 108 
 109 @interface ScrollAreaAccessibility : JavaComponentAccessibility {
 110 
 111 }
 112 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
 113 - (NSArray *)accessibilityContentsAttribute;
 114 - (BOOL)accessibilityIsContentsAttributeSettable;
 115 - (id)accessibilityVerticalScrollBarAttribute;
 116 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable;
 117 - (id)accessibilityHorizontalScrollBarAttribute;
 118 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable;
 119 @end
 120 
 121 @interface TableAccessibility : JavaComponentAccessibility {
 122 
 123 }
 124 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
 125 - (NSArray *)accessibilityRowsAttribute;
 126 - (NSArray *)accessibilityColumnsAttribute;
 127 @end
 128 
 129 
 130 @implementation JavaComponentAccessibility
 131 
 132 - (NSString *)description
 133 {
 134     return [NSString stringWithFormat:@"%@(title:'%@', desc:'%@', value:'%@')", [self accessibilityRoleAttribute],
 135         [self accessibilityTitleAttribute], [self accessibilityRoleDescriptionAttribute], [self accessibilityValueAttribute]];
 136 }
 137 
 138 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
 139 {
 140     self = [super init];
 141     if (self)
 142     {
 143         fParent = [parent retain];
 144         fView = [view retain];
 145         fJavaRole = [javaRole retain];
 146 
 147         fAccessible = (*env)->NewWeakGlobalRef(env, accessible);
 148         (*env)->ExceptionClear(env); // in case of OOME
 149         jobject jcomponent = [(AWTView *)fView awtComponent:env];
 150         fComponent = (*env)->NewWeakGlobalRef(env, jcomponent);
 151         (*env)->DeleteLocalRef(env, jcomponent);
 152 
 153         fIndex = index;
 154 
 155         fActions = nil;
 156         fActionsLOCK = [[NSObject alloc] init];
 157     }
 158     return self;
 159 }
 160 
 161 - (void)unregisterFromCocoaAXSystem
 162 {
 163     AWT_ASSERT_APPKIT_THREAD;
 164     static dispatch_once_t initialize_unregisterUniqueId_once;
 165     static void (*unregisterUniqueId)(id);
 166     dispatch_once(&initialize_unregisterUniqueId_once, ^{
 167         void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL);
 168         unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement");
 169     });
 170     if (unregisterUniqueId) unregisterUniqueId(self);
 171 }
 172 
 173 - (void)dealloc
 174 {
 175     [self unregisterFromCocoaAXSystem];
 176 
 177     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 178 
 179     (*env)->DeleteWeakGlobalRef(env, fAccessible);
 180     fAccessible = NULL;
 181 
 182     (*env)->DeleteWeakGlobalRef(env, fComponent);
 183     fComponent = NULL;
 184 
 185     [fParent release];
 186     fParent = nil;
 187 
 188     [fNSRole release];
 189     fNSRole = nil;
 190 
 191     [fJavaRole release];
 192     fJavaRole = nil;
 193 
 194     [fView release];
 195     fView = nil;
 196 
 197     [fActions release];
 198     fActions = nil;
 199 
 200     [fActionsLOCK release];
 201     fActionsLOCK = nil;
 202 
 203     [super dealloc];
 204 }
 205 
 206 - (void)postValueChanged
 207 {
 208     AWT_ASSERT_APPKIT_THREAD;
 209     NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification);
 210 }
 211 
 212 - (void)postSelectedTextChanged
 213 {
 214     AWT_ASSERT_APPKIT_THREAD;
 215     NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification);
 216 }
 217 
 218 - (void)postSelectionChanged
 219 {
 220     AWT_ASSERT_APPKIT_THREAD;
 221     NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification);
 222 }
 223 
 224 - (void)postMenuOpened
 225 {
 226     AWT_ASSERT_APPKIT_THREAD;
 227     NSAccessibilityPostNotification(self, (NSString *)kAXMenuOpenedNotification);
 228 }
 229 
 230 - (void)postMenuClosed
 231 {
 232     AWT_ASSERT_APPKIT_THREAD;
 233     NSAccessibilityPostNotification(self, (NSString *)kAXMenuClosedNotification);
 234 }
 235 
 236 - (void)postMenuItemSelected
 237 {
 238     AWT_ASSERT_APPKIT_THREAD;
 239     NSAccessibilityPostNotification(self, (NSString *)kAXMenuItemSelectedNotification);
 240 }
 241 
 242 - (BOOL)isEqual:(id)anObject
 243 {
 244     if (![anObject isKindOfClass:[self class]]) return NO;
 245     JavaComponentAccessibility *accessibility = (JavaComponentAccessibility *)anObject;
 246 
 247     JNIEnv* env = [ThreadUtilities getJNIEnv];
 248     return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible);
 249 }
 250 
 251 - (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible
 252 {
 253     return (*env)->IsSameObject(env, fAccessible, accessible);
 254 }
 255 
 256 + (void)initialize
 257 {
 258     if (sAttributeNamesForRoleCache == nil) {
 259         sAttributeNamesLOCK = [[NSObject alloc] init];
 260         sAttributeNamesForRoleCache = [[NSMutableDictionary alloc] initWithCapacity:60];
 261     }
 262 
 263     if (sRoles == nil) {
 264         initializeRoles();
 265     }
 266 
 267     if (sAccessibilityClass == NULL) {
 268         JNF_STATIC_MEMBER_CACHE(jm_getAccessibility, sjc_CAccessibility, "getAccessibility", "([Ljava/lang/String;)Lsun/lwawt/macosx/CAccessibility;");
 269 
 270 #ifdef JAVA_AX_NO_IGNORES
 271         NSArray *ignoredKeys = [NSArray array];
 272 #else
 273         NSArray *ignoredKeys = [sRoles allKeysForObject:JavaAccessibilityIgnore];
 274 #endif
 275         jobjectArray result = NULL;
 276         jsize count = [ignoredKeys count];
 277 
 278         JNIEnv *env = [ThreadUtilities getJNIEnv];
 279 
 280         static JNF_CLASS_CACHE(jc_String, "java/lang/String");
 281         result = JNFNewObjectArray(env, &jc_String, count);
 282         if (!result) {
 283             NSLog(@"In %s, can't create Java array of String objects", __FUNCTION__);
 284             return;
 285         }
 286 
 287         NSInteger i;
 288         for (i = 0; i < count; i++) {
 289             jstring jString = JNFNSToJavaString(env, [ignoredKeys objectAtIndex:i]);
 290             (*env)->SetObjectArrayElement(env, result, i, jString);
 291             (*env)->DeleteLocalRef(env, jString);
 292         }
 293 
 294         sAccessibilityClass = JNFCallStaticObjectMethod(env, jm_getAccessibility, result); // AWT_THREADING Safe (known object)
 295     }
 296 }
 297 
 298 + (void)postFocusChanged:(id)message
 299 {
 300     AWT_ASSERT_APPKIT_THREAD;
 301     NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification);
 302 }
 303 
 304 + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
 305     if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) {
 306         return jaccessible;
 307     } else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) {
 308         return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible);
 309     }
 310     return NULL;
 311 }
 312 
 313 + (NSArray *)childrenOfParent:(JavaComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored
 314 {
 315     if (parent->fAccessible == NULL) return nil;
 316     jobjectArray jchildrenAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
 317     if (jchildrenAndRoles == NULL) return nil;
 318 
 319     jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles);
 320     NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child
 321 
 322     NSInteger i;
 323     NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly
 324     for(i = 0; i < arrayLen; i+=2)
 325     {
 326         jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i);
 327         jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1);
 328 
 329         NSString *childJavaRole = nil;
 330         if (jchildJavaRole != NULL) {
 331             jobject jkey = JNFGetObjectField(env, jchildJavaRole, sjf_key);
 332             childJavaRole = JNFJavaToNSString(env, jkey);
 333             (*env)->DeleteLocalRef(env, jkey);
 334         }
 335 
 336         JavaComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView];
 337 
 338         (*env)->DeleteLocalRef(env, jchild);
 339         (*env)->DeleteLocalRef(env, jchildJavaRole);
 340 
 341         [children addObject:child];
 342         childIndex++;
 343     }
 344     (*env)->DeleteLocalRef(env, jchildrenAndRoles);
 345 
 346     return children;
 347 }
 348 
 349 + (JavaComponentAccessibility *)createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view
 350 {
 351     JavaComponentAccessibility *ret = nil;
 352     jobject jcomponent = [(AWTView *)view awtComponent:env];
 353     jint index = JNFCallStaticIntMethod(env, sjm_getAccessibleIndexInParent, jaccessible, jcomponent);
 354     if (index >= 0) {
 355       NSString *javaRole = getJavaRole(env, jaccessible, jcomponent);
 356       ret = [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view];
 357     }
 358     (*env)->DeleteLocalRef(env, jcomponent);
 359     return ret;
 360 }
 361 
 362 + (JavaComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
 363 {
 364     return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view];
 365 }
 366 
 367 + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view
 368 {
 369     // try to fetch the jCAX from Java, and return autoreleased
 370     jobject jCAX = [JavaComponentAccessibility getCAccessible:jaccessible withEnv:env];
 371     if (jCAX == NULL) return nil;
 372     JavaComponentAccessibility *value = (JavaComponentAccessibility *) jlong_to_ptr(JNFGetLongField(env, jCAX, jf_ptr));
 373     if (value != nil) {
 374         (*env)->DeleteLocalRef(env, jCAX);
 375         return [[value retain] autorelease];
 376     }
 377 
 378     // otherwise, create a new instance
 379     JavaComponentAccessibility *newChild = nil;
 380     if ([javaRole isEqualToString:@"pagetablist"]) {
 381         newChild = [TabGroupAccessibility alloc];
 382     } else if ([javaRole isEqualToString:@"table"]) {
 383         newChild = [TableAccessibility alloc];
 384     } else if ([javaRole isEqualToString:@"scrollpane"]) {
 385         newChild = [ScrollAreaAccessibility alloc];
 386     } else {
 387         NSString *nsRole = [sRoles objectForKey:javaRole];
 388         if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || [nsRole isEqualToString:NSAccessibilityTextAreaRole] || [nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
 389             newChild = [JavaTextAccessibility alloc];
 390         } else {
 391             newChild = [JavaComponentAccessibility alloc];
 392         }
 393     }
 394 
 395     // must init freshly -alloc'd object
 396     [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance
 397 
 398     // If creating a JPopupMenu (not a combobox popup list) need to fire menuOpened.
 399     // This is the only way to know if the menu is opening; visible state change
 400     // can't be caught because the listeners are not set up in time.
 401     if ( [javaRole isEqualToString:@"popupmenu"] &&
 402          ![[parent javaRole] isEqualToString:@"combobox"] ) {
 403         [newChild postMenuOpened];
 404     }
 405 
 406     // must hard retain pointer poked into Java object
 407     [newChild retain];
 408     JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild));
 409     (*env)->DeleteLocalRef(env, jCAX);
 410 
 411     // return autoreleased instance
 412     return [newChild autorelease];
 413 }
 414 
 415 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
 416 {
 417     static JNF_STATIC_MEMBER_CACHE(jm_getInitialAttributeStates, sjc_CAccessibility, "getInitialAttributeStates", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[Z");
 418 
 419     NSMutableArray *attributeNames = [NSMutableArray arrayWithCapacity:20];
 420     [attributeNames retain];
 421 
 422     // all elements respond to parent, role, role description, window, topLevelUIElement, help
 423     [attributeNames addObject:NSAccessibilityParentAttribute];
 424     [attributeNames addObject:NSAccessibilityRoleAttribute];
 425     [attributeNames addObject:NSAccessibilityRoleDescriptionAttribute];
 426     [attributeNames addObject:NSAccessibilityHelpAttribute];
 427 
 428     // cmcnote: AXMenu usually doesn't respond to window / topLevelUIElement. But menus within a Java app's window
 429     // probably should. Should we use some role other than AXMenu / AXMenuBar for Java menus?
 430     [attributeNames addObject:NSAccessibilityWindowAttribute];
 431     [attributeNames addObject:NSAccessibilityTopLevelUIElementAttribute];
 432 
 433     // set accessible subrole
 434     NSString *javaRole = [self javaRole];
 435     if (javaRole != nil && [javaRole isEqualToString:@"passwordtext"]) {
 436         //cmcnote: should turn this into a constant
 437         [attributeNames addObject:NSAccessibilitySubroleAttribute];
 438     }
 439 
 440     // Get all the other accessibility attributes states we need in one swell foop.
 441     // javaRole isn't pulled in because we need protected access to AccessibleRole.key
 442     jbooleanArray attributeStates = (jbooleanArray)JNFCallStaticObjectMethod(env, jm_getInitialAttributeStates, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 443     if (attributeStates == NULL) return nil;
 444     jboolean *attributeStatesArray = (*env)->GetBooleanArrayElements(env, attributeStates, 0);
 445     if (attributeStatesArray == NULL) {
 446         // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck.
 447         NSLog(@"%s failed calling GetBooleanArrayElements", __FUNCTION__);
 448         return nil;
 449     }
 450 
 451     // if there's a component, it can be enabled and it has a size/position
 452     if (attributeStatesArray[0]) {
 453         [attributeNames addObject:NSAccessibilityEnabledAttribute];
 454         [attributeNames addObject:NSAccessibilitySizeAttribute];
 455         [attributeNames addObject:NSAccessibilityPositionAttribute];
 456     }
 457 
 458     // According to javadoc, a component that is focusable will return true from isFocusTraversable,
 459     // as well as having AccessibleState.FOCUSABLE in it's AccessibleStateSet.
 460     // We use the former heuristic; if the component focus-traversable, add a focused attribute
 461     // See also: accessibilityIsFocusedAttributeSettable
 462     if (attributeStatesArray[1])
 463     {
 464         [attributeNames addObject:NSAccessibilityFocusedAttribute];
 465     }
 466 
 467     // if it's a pagetab / radiobutton, it has a value but no min/max value.
 468     BOOL hasAxValue = attributeStatesArray[2];
 469     if ([javaRole isEqualToString:@"pagetab"] || [javaRole isEqualToString:@"radiobutton"]) {
 470         [attributeNames addObject:NSAccessibilityValueAttribute];
 471     } else {
 472         // if not a pagetab/radio button, and it has a value, it has a min/max/current value.
 473         if (hasAxValue) {
 474             // er, it has a min/max/current value if it's not a button.
 475             // See AppKit/NSButtonCellAccessibility.m
 476             if (![javaRole isEqualToString:@"pushbutton"]) {
 477                 //cmcnote: make this (and "passwordtext") constants instead of magic strings
 478                 [attributeNames addObject:NSAccessibilityMinValueAttribute];
 479                 [attributeNames addObject:NSAccessibilityMaxValueAttribute];
 480                 [attributeNames addObject:NSAccessibilityValueAttribute];
 481             }
 482         }
 483     }
 484 
 485     // does it have an orientation?
 486     if (attributeStatesArray[4]) {
 487         [attributeNames addObject:NSAccessibilityOrientationAttribute];
 488     }
 489 
 490     // name
 491     if (attributeStatesArray[5]) {
 492         [attributeNames addObject:NSAccessibilityTitleAttribute];
 493     }
 494 
 495     // children
 496     if (attributeStatesArray[6]) {
 497         [attributeNames addObject:NSAccessibilityChildrenAttribute];
 498         if ([javaRole isEqualToString:@"list"]
 499                 || [javaRole isEqualToString:@"table"]) {
 500             [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute];
 501             [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute];
 502         }
 503         // Just above, the below mentioned support has been added back in for lists.
 504         // However, the following comments may still be useful for future fixes.
 505 //        [attributeNames addObject:NSAccessibilitySelectedChildrenAttribute];
 506 //        [attributeNames addObject:NSAccessibilityVisibleChildrenAttribute];
 507                 //According to AXRoles.txt:
 508                 //VisibleChildren: radio group, list, row, table row subrole
 509                 //SelectedChildren: list
 510     }
 511 
 512     // Cleanup
 513     (*env)->ReleaseBooleanArrayElements(env, attributeStates, attributeStatesArray, JNI_ABORT);
 514 
 515     return attributeNames;
 516 }
 517 
 518 - (NSDictionary *)getActions:(JNIEnv *)env
 519 {
 520     @synchronized(fActionsLOCK) {
 521         if (fActions == nil) {
 522             fActions = [[NSMutableDictionary alloc] initWithCapacity:3];
 523             [self getActionsWithEnv:env];
 524         }
 525     }
 526 
 527     return fActions;
 528 }
 529 
 530 - (void)getActionsWithEnv:(JNIEnv *)env
 531 {
 532     static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;");
 533 
 534     // On MacOSX, text doesn't have actions, in java it does.
 535     // cmcnote: NOT TRUE - Editable text has AXShowMenu. Textfields have AXConfirm. Static text has no actions.
 536     jobject axAction = JNFCallStaticObjectMethod(env, jm_getAccessibleAction, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 537     if (axAction != NULL) {
 538         //+++gdb NOTE: In MacOSX, there is just a single Action, not multiple. In java,
 539         //  the first one seems to be the most basic, so this will be used.
 540         // cmcnote: NOT TRUE - Sometimes there are multiple actions, eg sliders have AXDecrement AND AXIncrement (radr://3893192)
 541         JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:0 withComponent:fComponent];
 542         [fActions setObject:action forKey:[self isMenu] ? NSAccessibilityPickAction : NSAccessibilityPressAction];
 543         [action release];
 544         (*env)->DeleteLocalRef(env, axAction);
 545     }
 546 }
 547 
 548 - (jobject)axContextWithEnv:(JNIEnv *)env
 549 {
 550     return getAxContext(env, fAccessible, fComponent);
 551 }
 552 
 553 - (id)parent
 554 {
 555     static JNF_CLASS_CACHE(sjc_Window, "java/awt/Window");
 556     static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
 557     static JNF_STATIC_MEMBER_CACHE(sjm_getSwingAccessible, sjc_CAccessible, "getSwingAccessible", "(Ljavax/accessibility/Accessible;)Ljavax/accessibility/Accessible;");
 558 
 559     if(fParent == nil) {
 560         JNIEnv* env = [ThreadUtilities getJNIEnv];
 561 
 562         jobject jparent = JNFCallStaticObjectMethod(env, sjm_getAccessibleParent, fAccessible, fComponent);
 563 
 564         if (jparent == NULL) {
 565             fParent = fView;
 566         } else {
 567             AWTView *view = fView;
 568             jobject jax = JNFCallStaticObjectMethod(env, sjm_getSwingAccessible, fAccessible);
 569 
 570             if (JNFIsInstanceOf(env, jax, &sjc_Window)) {
 571                 // In this case jparent is an owner toplevel and we should retrieve its own view
 572                 view = [AWTView awtView:env ofAccessible:jparent];
 573             }
 574             if (view != nil) {
 575                 fParent = [JavaComponentAccessibility createWithAccessible:jparent withEnv:env withView:view];
 576             }
 577             if (fParent == nil) {
 578                 fParent = fView;
 579             }
 580             (*env)->DeleteLocalRef(env, jparent);
 581             (*env)->DeleteLocalRef(env, jax );
 582         }
 583         [fParent retain];
 584     }
 585     return fParent;
 586 }
 587 
 588 - (NSView *)view
 589 {
 590     return fView;
 591 }
 592 
 593 - (NSWindow *)window
 594 {
 595     return [[self view] window];
 596 }
 597 
 598 - (NSString *)javaRole
 599 {
 600     if(fJavaRole == nil) {
 601         JNIEnv* env = [ThreadUtilities getJNIEnv];
 602         fJavaRole = getJavaRole(env, fAccessible, fComponent);
 603         [fJavaRole retain];
 604     }
 605     return fJavaRole;
 606 }
 607 
 608 - (BOOL)isMenu
 609 {
 610     id role = [self accessibilityRoleAttribute];
 611     return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole];
 612 }
 613 
 614 - (BOOL)isSelected:(JNIEnv *)env
 615 {
 616     if (fIndex == -1) {
 617         return NO;
 618     }
 619 
 620     return isChildSelected(env, ((JavaComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent);
 621 }
 622 
 623 - (BOOL)isSelectable:(JNIEnv *)env
 624 {
 625     jobject axContext = [self axContextWithEnv:env];
 626     BOOL selectable = isSelectable(env, axContext, fComponent);
 627     (*env)->DeleteLocalRef(env, axContext);
 628     return selectable;
 629 }
 630 
 631 - (BOOL)isVisible:(JNIEnv *)env
 632 {
 633     if (fIndex == -1) {
 634         return NO;
 635     }
 636 
 637     jobject axContext = [self axContextWithEnv:env];
 638     BOOL showing = isShowing(env, axContext, fComponent);
 639     (*env)->DeleteLocalRef(env, axContext);
 640     return showing;
 641 }
 642 
 643 // the array of names for each role is cached in the sAttributeNamesForRoleCache
 644 - (NSArray *)accessibilityAttributeNames
 645 {
 646     JNIEnv* env = [ThreadUtilities getJNIEnv];
 647 
 648     @synchronized(sAttributeNamesLOCK) {
 649         NSString *javaRole = [self javaRole];
 650         NSArray *names =
 651             (NSArray *)[sAttributeNamesForRoleCache objectForKey:javaRole];
 652         if (names == nil) {
 653             names = [self initializeAttributeNamesWithEnv:env];
 654 #ifdef JAVA_AX_DEBUG
 655             NSLog(@"Initializing: %s for %@: %@", __FUNCTION__, javaRole, names);
 656 #endif
 657             [sAttributeNamesForRoleCache setObject:names forKey:javaRole];
 658         }
 659         // The above set of attributes is immutable per role, but some objects, if
 660         // they are the child of a list, need to add the selected and index attributes.
 661         if ([self accessibilityIsIgnored]) {
 662             return names;
 663         }
 664         id myParent = [self accessibilityParentAttribute];
 665         if ([myParent isKindOfClass:[JavaComponentAccessibility class]]) {
 666             NSString *parentRole = [(JavaComponentAccessibility *)myParent javaRole];
 667 
 668             if ([parentRole isEqualToString:@"list"]
 669                     || [parentRole isEqualToString:@"table"]) {
 670                 NSMutableArray *moreNames =
 671                     [[NSMutableArray alloc] initWithCapacity: [names count] + 2];
 672                 [moreNames addObjectsFromArray: names];
 673                 [moreNames addObject:NSAccessibilitySelectedAttribute];
 674                 [moreNames addObject:NSAccessibilityIndexAttribute];
 675                 return moreNames;
 676             }
 677         }
 678         // popupmenu's return values not selected children
 679         if ( [javaRole isEqualToString:@"popupmenu"] &&
 680              ![[[self parent] javaRole] isEqualToString:@"combobox"] ) {
 681             NSMutableArray *moreNames =
 682                 [[NSMutableArray alloc] initWithCapacity: [names count] + 1];
 683             [moreNames addObjectsFromArray: names];
 684             [moreNames addObject:NSAccessibilityValueAttribute];
 685             return moreNames;
 686         }
 687         return names;
 688 
 689     }  // end @synchronized
 690 
 691 #ifdef JAVA_AX_DEBUG
 692     NSLog(@"Warning in %s: could not find attribute names for role: %@", __FUNCTION__, [self javaRole]);
 693 #endif
 694 
 695     return nil;
 696 }
 697 
 698 // -- accessibility attributes --
 699 
 700 - (BOOL)accessibilityShouldUseUniqueId {
 701     return YES;
 702 }
 703 
 704 - (BOOL)accessibilitySupportsOverriddenAttributes {
 705     return YES;
 706 }
 707 
 708 
 709 // generic getters & setters
 710 // cmcnote: it would make more sense if these generic getters/setters were in JavaAccessibilityUtilities
 711 - (id)accessibilityAttributeValue:(NSString *)attribute
 712 {
 713     AWT_ASSERT_APPKIT_THREAD;
 714 
 715     // turns attribute "NSAccessibilityEnabledAttribute" into getter "accessibilityEnabledAttribute",
 716     // calls getter on self
 717     return JavaAccessibilityAttributeValue(self, attribute);
 718 }
 719 
 720 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute
 721 {
 722     AWT_ASSERT_APPKIT_THREAD;
 723 
 724     // turns attribute "NSAccessibilityParentAttribute" into selector "accessibilityIsParentAttributeSettable",
 725     // calls selector on self
 726     return JavaAccessibilityIsAttributeSettable(self, attribute);
 727 }
 728 
 729 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
 730 {
 731     AWT_ASSERT_APPKIT_THREAD;
 732 
 733     if ([self accessibilityIsAttributeSettable:attribute]) {
 734         // turns attribute "NSAccessibilityFocusAttribute" into setter "accessibilitySetFocusAttribute",
 735         // calls setter on self
 736         JavaAccessibilitySetAttributeValue(self, attribute, value);
 737     }
 738 }
 739 
 740 
 741 // specific attributes, in alphabetical order a la
 742 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
 743 
 744 // Elements that current element contains (NSArray)
 745 - (NSArray *)accessibilityChildrenAttribute
 746 {
 747     JNIEnv* env = [ThreadUtilities getJNIEnv];
 748     NSArray *children = [JavaComponentAccessibility childrenOfParent:self
 749                                                     withEnv:env
 750                                                     withChildrenCode:JAVA_AX_ALL_CHILDREN
 751                                                     allowIgnored:NO];
 752 
 753     NSArray *value = nil;
 754     if ([children count] > 0) {
 755         value = children;
 756     }
 757 
 758     return value;
 759 }
 760 
 761 - (BOOL)accessibilityIsChildrenAttributeSettable
 762 {
 763     return NO;
 764 }
 765 
 766 - (NSUInteger)accessibilityIndexOfChild:(id)child
 767 {
 768     // Only special-casing for Lists, for now. This allows lists to be accessible, fixing radr://3856139 "JLists are broken".
 769     // Will probably want to special-case for Tables when we implement them (radr://3096643 "Accessibility: Table").
 770     // In AppKit, NSMatrixAccessibility (which uses NSAccessibilityListRole), NSTableRowAccessibility, and NSTableViewAccessibility are the
 771     // only ones that override the default implementation in NSAccessibility
 772     if (![[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityListRole]) {
 773         return [super accessibilityIndexOfChild:child];
 774     }
 775 
 776     jint returnValue =
 777         JNFCallStaticIntMethod( [ThreadUtilities getJNIEnv],
 778                                 sjm_getAccessibleIndexInParent,
 779                                 ((JavaComponentAccessibility *)child)->fAccessible,
 780                                 ((JavaComponentAccessibility *)child)->fComponent );
 781     return (returnValue == -1) ? NSNotFound : returnValue;
 782 }
 783 
 784 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
 785 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
 786     if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
 787         // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
 788         NSArray *child = [JavaComponentAccessibility childrenOfParent:self withEnv:[ThreadUtilities getJNIEnv] withChildrenCode:(NSInteger)index allowIgnored:NO];
 789         if ([child count] > 0) {
 790             return child;
 791         }
 792     }
 793     return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
 794 }
 795 
 796 // Flag indicating enabled state of element (NSNumber)
 797 - (NSNumber *)accessibilityEnabledAttribute
 798 {
 799     static JNF_STATIC_MEMBER_CACHE(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z");
 800 
 801     JNIEnv* env = [ThreadUtilities getJNIEnv];
 802     NSNumber *value = [NSNumber numberWithBool:JNFCallStaticBooleanMethod(env, jm_isEnabled, fAccessible, fComponent)]; // AWT_THREADING Safe (AWTRunLoop)
 803     if (value == nil) {
 804         NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self);
 805     }
 806     return value;
 807 }
 808 
 809 - (BOOL)accessibilityIsEnabledAttributeSettable
 810 {
 811     return NO;
 812 }
 813 
 814 // Flag indicating presence of keyboard focus (NSNumber)
 815 - (NSNumber *)accessibilityFocusedAttribute
 816 {
 817     if ([self accessibilityIsFocusedAttributeSettable]) {
 818         return [NSNumber numberWithBool:[self isEqual:[NSApp accessibilityFocusedUIElement]]];
 819     }
 820     return [NSNumber numberWithBool:NO];
 821 }
 822 
 823 - (BOOL)accessibilityIsFocusedAttributeSettable
 824 {
 825     JNIEnv* env = [ThreadUtilities getJNIEnv];
 826     // According to javadoc, a component that is focusable will return true from isFocusTraversable,
 827     // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet.
 828     // We use the former heuristic; if the component focus-traversable, add a focused attribute
 829     // See also initializeAttributeNamesWithEnv:
 830     if (JNFCallStaticBooleanMethod(env, sjm_isFocusTraversable, fAccessible, fComponent)) { // AWT_THREADING Safe (AWTRunLoop)
 831         return YES;
 832     }
 833 
 834     return NO;
 835 }
 836 
 837 - (void)accessibilitySetFocusedAttribute:(id)value
 838 {
 839     static JNF_STATIC_MEMBER_CACHE(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V");
 840 
 841     if ([(NSNumber*)value boolValue])
 842     {
 843         JNIEnv* env = [ThreadUtilities getJNIEnv];
 844         JNFCallStaticVoidMethod(env, jm_requestFocus, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 845     }
 846 }
 847 
 848 // Instance description, such as a help tag string (NSString)
 849 - (NSString *)accessibilityHelpAttribute
 850 {
 851     JNIEnv* env = [ThreadUtilities getJNIEnv];
 852 
 853     jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleDescription, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 854     if (val == NULL) {
 855         return nil;
 856     }
 857     NSString* str = JNFJavaToNSString(env, val);
 858     (*env)->DeleteLocalRef(env, val);
 859     return str;
 860 }
 861 
 862 - (BOOL)accessibilityIsHelpAttributeSettable
 863 {
 864     return NO;
 865 }
 866 
 867 - (NSValue *)accessibilityIndexAttribute
 868 {
 869     NSInteger index = fIndex;
 870     NSValue *returnValue = [NSValue value:&index withObjCType:@encode(NSInteger)];
 871     return returnValue;
 872 }
 873 
 874 - (BOOL)accessibilityIsIndexAttributeSettable
 875 {
 876     return NO;
 877 }
 878 
 879 // Element's maximum value (id)
 880 - (id)accessibilityMaxValueAttribute
 881 {
 882     static JNF_STATIC_MEMBER_CACHE(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
 883 
 884     JNIEnv* env = [ThreadUtilities getJNIEnv];
 885 
 886     jobject axValue = JNFCallStaticObjectMethod(env, jm_getMaximumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 887     if (axValue == NULL) {
 888         return [NSNumber numberWithInt:0];
 889     }
 890     NSNumber* num = JNFJavaToNSNumber(env, axValue);
 891     (*env)->DeleteLocalRef(env, axValue);
 892     return num;
 893 }
 894 
 895 - (BOOL)accessibilityIsMaxValueAttributeSettable
 896 {
 897     return NO;
 898 }
 899 
 900 // Element's minimum value (id)
 901 - (id)accessibilityMinValueAttribute
 902 {
 903     static JNF_STATIC_MEMBER_CACHE(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;");
 904 
 905     JNIEnv* env = [ThreadUtilities getJNIEnv];
 906 
 907     jobject axValue = JNFCallStaticObjectMethod(env, jm_getMinimumAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 908     if (axValue == NULL) {
 909         return [NSNumber numberWithInt:0];
 910     }
 911     NSNumber* num = JNFJavaToNSNumber(env, axValue);
 912     (*env)->DeleteLocalRef(env, axValue);
 913     return num;
 914 }
 915 
 916 - (BOOL)accessibilityIsMinValueAttributeSettable
 917 {
 918     return NO;
 919 }
 920 
 921 - (id)accessibilityOrientationAttribute
 922 {
 923     JNIEnv* env = [ThreadUtilities getJNIEnv];
 924     jobject axContext = [self axContextWithEnv:env];
 925 
 926     // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz
 927     if (isVertical(env, axContext, fComponent)) {
 928         (*env)->DeleteLocalRef(env, axContext);
 929         return NSAccessibilityVerticalOrientationValue;
 930     }
 931 
 932     if (isHorizontal(env, axContext, fComponent)) {
 933         (*env)->DeleteLocalRef(env, axContext);
 934         return NSAccessibilityHorizontalOrientationValue;
 935     }
 936 
 937     (*env)->DeleteLocalRef(env, axContext);
 938     return nil;
 939 }
 940 
 941 - (BOOL)accessibilityIsOrientationAttributeSettable
 942 {
 943     return NO;
 944 }
 945 
 946 // Element containing current element (id)
 947 - (id)accessibilityParentAttribute
 948 {
 949     return NSAccessibilityUnignoredAncestor([self parent]);
 950 }
 951 
 952 - (BOOL)accessibilityIsParentAttributeSettable
 953 {
 954     return NO;
 955 }
 956 
 957 // Screen position of element's lower-left corner in lower-left relative screen coordinates (NSValue)
 958 - (NSValue *)accessibilityPositionAttribute
 959 {
 960     JNIEnv* env = [ThreadUtilities getJNIEnv];
 961     jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
 962 
 963     // NSAccessibility wants the bottom left point of the object in
 964     // bottom left based screen coords
 965 
 966     // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent.
 967     NSSize size = getAxComponentSize(env, axComponent, fComponent);
 968     NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent);
 969     (*env)->DeleteLocalRef(env, axComponent);
 970 
 971     point.y += size.height;
 972 
 973     // Now make it into Cocoa screen coords.
 974     point.y = [[[[self view] window] screen] frame].size.height - point.y;
 975 
 976     return [NSValue valueWithPoint:point];
 977 }
 978 
 979 - (BOOL)accessibilityIsPositionAttributeSettable
 980 {
 981     // 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
 982     // We *could* make use of Java's AccessibleComponent.setLocation() method. Investigate. radr://3953869
 983     return NO;
 984 }
 985 
 986 // Element type, such as NSAccessibilityRadioButtonRole (NSString). See the role table
 987 // at http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
 988 - (NSString *)accessibilityRoleAttribute
 989 {
 990     if (fNSRole == nil) {
 991         NSString *javaRole = [self javaRole];
 992         fNSRole = [sRoles objectForKey:javaRole];
 993         // The sRoles NSMutableDictionary maps popupmenu to Mac's popup button.
 994         // JComboBox behavior currently relies on this.  However this is not the
 995         // proper mapping for a JPopupMenu so fix that.
 996         if ( [javaRole isEqualToString:@"popupmenu"] &&
 997              ![[[self parent] javaRole] isEqualToString:@"combobox"] ) {
 998              fNSRole = NSAccessibilityMenuRole;
 999         }
1000         if (fNSRole == nil) {
1001             // this component has assigned itself a custom AccessibleRole not in the sRoles array
1002             fNSRole = javaRole;
1003         }
1004         [fNSRole retain];
1005     }
1006     return fNSRole;
1007 }
1008 
1009 - (BOOL)accessibilityIsRoleAttributeSettable
1010 {
1011     return NO;
1012 }
1013 
1014 // Localized, user-readable description of role, such as radio button (NSString)
1015 - (NSString *)accessibilityRoleDescriptionAttribute
1016 {
1017     // first ask AppKit for its accessible role description for a given AXRole
1018     NSString *value = NSAccessibilityRoleDescription([self accessibilityRoleAttribute], nil);
1019 
1020     if (value == nil) {
1021         // query java if necessary
1022         static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;");
1023 
1024         JNIEnv* env = [ThreadUtilities getJNIEnv];
1025 
1026         jobject axRole = JNFCallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent);
1027         if (axRole != NULL) {
1028             value = JNFJavaToNSString(env, axRole);
1029             (*env)->DeleteLocalRef(env, axRole);
1030         } else {
1031             value = @"unknown";
1032         }
1033     }
1034 
1035     return value;
1036 }
1037 
1038 - (BOOL)accessibilityIsRoleDescriptionAttributeSettable
1039 {
1040     return NO;
1041 }
1042 
1043 // Currently selected children (NSArray)
1044 - (NSArray *)accessibilitySelectedChildrenAttribute
1045 {
1046     JNIEnv* env = [ThreadUtilities getJNIEnv];
1047     NSArray *selectedChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_SELECTED_CHILDREN allowIgnored:NO];
1048     if ([selectedChildren count] > 0) {
1049         return selectedChildren;
1050     }
1051 
1052     return nil;
1053 }
1054 
1055 - (BOOL)accessibilityIsSelectedChildrenAttributeSettable
1056 {
1057     return NO; // cmcnote: actually it should be. so need to write accessibilitySetSelectedChildrenAttribute also
1058 }
1059 
1060 - (NSNumber *)accessibilitySelectedAttribute
1061 {
1062     return [NSNumber numberWithBool:[self isSelected:[ThreadUtilities getJNIEnv]]];
1063 }
1064 
1065 - (BOOL)accessibilityIsSelectedAttributeSettable
1066 {
1067     if ([self isSelectable:[ThreadUtilities getJNIEnv]]) {
1068         return YES;
1069     } else {
1070         return NO;
1071     }
1072 }
1073 
1074 - (void)accessibilitySetSelectedAttribute:(id)value
1075 {
1076     static JNF_STATIC_MEMBER_CACHE( jm_requestSelection,
1077                                     sjc_CAccessibility,
1078                                     "requestSelection",
1079                                     "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V" );
1080 
1081     if ([(NSNumber*)value boolValue]) {
1082         JNIEnv* env = [ThreadUtilities getJNIEnv];
1083         JNFCallStaticVoidMethod(env, jm_requestSelection, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1084     }
1085 }
1086 
1087 // Element size (NSValue)
1088 - (NSValue *)accessibilitySizeAttribute {
1089     JNIEnv* env = [ThreadUtilities getJNIEnv];
1090     jobject axComponent = JNFCallStaticObjectMethod(env, sjm_getAccessibleComponent, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1091     NSValue* size = [NSValue valueWithSize:getAxComponentSize(env, axComponent, fComponent)];
1092     (*env)->DeleteLocalRef(env, axComponent);
1093     return size;
1094 }
1095 
1096 - (BOOL)accessibilityIsSizeAttributeSettable
1097 {
1098     // SIZE is settable in windows if [self styleMask] & NSResizableWindowMask - but windows are heavyweight so we're ok here
1099     // SIZE is settable in columns if [[self tableValue] allowsColumnResizing - haven't dealt with columns yet
1100     return NO;
1101 }
1102 
1103 // Element subrole type, such as NSAccessibilityTableRowSubrole (NSString). See the subrole attribute table at
1104 // http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Protocols/NSAccessibility.html
1105 - (NSString *)accessibilitySubroleAttribute
1106 {
1107     NSString *value = nil;
1108     if ([[self javaRole] isEqualToString:@"passwordtext"]) {
1109         value = NSAccessibilitySecureTextFieldSubrole;
1110     }
1111     /*
1112     // other subroles. TableRow and OutlineRow may be relevant to us
1113      NSAccessibilityCloseButtonSubrole // no, heavyweight window takes care of this
1114      NSAccessibilityMinimizeButtonSubrole // "
1115      NSAccessibilityOutlineRowSubrole    // maybe?
1116      NSAccessibilitySecureTextFieldSubrole // currently used
1117      NSAccessibilityTableRowSubrole        // maybe?
1118      NSAccessibilityToolbarButtonSubrole // maybe?
1119      NSAccessibilityUnknownSubrole
1120      NSAccessibilityZoomButtonSubrole    // no, heavyweight window takes care of this
1121      NSAccessibilityStandardWindowSubrole// no, heavyweight window takes care of this
1122      NSAccessibilityDialogSubrole        // maybe?
1123      NSAccessibilitySystemDialogSubrole    // no
1124      NSAccessibilityFloatingWindowSubrole // in 1.5 if we implement these, heavyweight will take care of them anyway
1125      NSAccessibilitySystemFloatingWindowSubrole
1126      NSAccessibilityIncrementArrowSubrole  // no
1127      NSAccessibilityDecrementArrowSubrole  // no
1128      NSAccessibilityIncrementPageSubrole   // no
1129      NSAccessibilityDecrementPageSubrole   // no
1130      NSAccessibilitySearchFieldSubrole    //no
1131      */
1132     return value;
1133 }
1134 
1135 - (BOOL)accessibilityIsSubroleAttributeSettable
1136 {
1137     return NO;
1138 }
1139 
1140 // Title of element, such as button text (NSString)
1141 - (NSString *)accessibilityTitleAttribute
1142 {
1143     // Return empty string for labels, since their value and tile end up being the same thing and this leads to repeated text.
1144     if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) {
1145         return @"";
1146     }
1147 
1148     JNIEnv* env = [ThreadUtilities getJNIEnv];
1149 
1150     jobject val = JNFCallStaticObjectMethod(env, sjm_getAccessibleName, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1151     if (val == NULL) {
1152         return nil;
1153     }
1154     NSString* str = JNFJavaToNSString(env, val);
1155     (*env)->DeleteLocalRef(env, val);
1156     return str;
1157 }
1158 
1159 - (BOOL)accessibilityIsTitleAttributeSettable
1160 {
1161     return NO;
1162 }
1163 
1164 - (NSWindow *)accessibilityTopLevelUIElementAttribute
1165 {
1166     return [self window];
1167 }
1168 
1169 - (BOOL)accessibilityIsTopLevelUIElementAttributeSettable
1170 {
1171     return NO;
1172 }
1173 
1174 // Element's value (id)
1175 // note that the appKit meaning of "accessibilityValue" is different from the java
1176 // meaning of "accessibleValue", which is specific to numerical values
1177 // (https://docs.oracle.com/javase/8/docs/api/javax/accessibility/AccessibleValue.html#setCurrentAccessibleValue-java.lang.Number-)
1178 - (id)accessibilityValueAttribute
1179 {
1180     static JNF_STATIC_MEMBER_CACHE(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;");
1181 
1182     JNIEnv* env = [ThreadUtilities getJNIEnv];
1183 
1184     // Need to handle popupmenus differently.
1185     //
1186     // At least for now don't handle combo box menus.
1187     // This may change when later fixing issues which currently
1188     // exist for combo boxes, but for now the following is only
1189     // for JPopupMenus, not for combobox menus.
1190     id parent = [self parent];
1191     if ( [[self javaRole] isEqualToString:@"popupmenu"] &&
1192          ![[parent javaRole] isEqualToString:@"combobox"] ) {
1193         NSArray *children =
1194             [JavaComponentAccessibility childrenOfParent:self
1195                                         withEnv:env
1196                                         withChildrenCode:JAVA_AX_ALL_CHILDREN
1197                                         allowIgnored:YES];
1198         if ([children count] > 0) {
1199             // handle case of AXMenuItem
1200             // need to ask menu what is selected
1201             NSArray *selectedChildrenOfMenu =
1202                 [self accessibilitySelectedChildrenAttribute];
1203             JavaComponentAccessibility *selectedMenuItem =
1204                 [selectedChildrenOfMenu objectAtIndex:0];
1205             if (selectedMenuItem != nil) {
1206                 jobject itemValue =
1207                         JNFCallStaticObjectMethod( env,
1208                                                    sjm_getAccessibleName,
1209                                                    selectedMenuItem->fAccessible,
1210                                                    selectedMenuItem->fComponent ); // AWT_THREADING Safe (AWTRunLoop)
1211                 if (itemValue == NULL) {
1212                     return nil;
1213                 }
1214                 NSString* itemString = JNFJavaToNSString(env, itemValue);
1215                 (*env)->DeleteLocalRef(env, itemValue);
1216                 return itemString;
1217             } else {
1218                 return nil;
1219             }
1220         }
1221     }
1222 
1223     // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value
1224     // a text value is taken care of in JavaTextAccessibility
1225 
1226     // cmcnote should coalesce these calls into one java call
1227     NSNumber *num = nil;
1228     jobject axValue = JNFCallStaticObjectMethod(env, sjm_getAccessibleValue, fAccessible, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1229     if (axValue != NULL) {
1230         jobject str = JNFCallStaticObjectMethod(env, jm_getCurrentAccessibleValue, axValue, fComponent);
1231         if (str != NULL) {
1232             num = JNFJavaToNSNumber(env, str); // AWT_THREADING Safe (AWTRunLoop)
1233             (*env)->DeleteLocalRef(env, str);
1234         }
1235         (*env)->DeleteLocalRef(env, axValue);
1236     }
1237     if (num == nil) {
1238         num = [NSNumber numberWithInt:0];
1239     }
1240     return num;
1241 }
1242 
1243 - (BOOL)accessibilityIsValueAttributeSettable
1244 {
1245     // according ot AppKit sources, in general the value attribute is not settable, except in the cases
1246     // of an NSScroller, an NSSplitView, and text that's both enabled & editable
1247     BOOL isSettable = NO;
1248     NSString *role = [self accessibilityRoleAttribute];
1249 
1250     if ([role isEqualToString:NSAccessibilityScrollBarRole] || // according to NSScrollerAccessibility
1251         [role isEqualToString:NSAccessibilitySplitGroupRole] ) // according to NSSplitViewAccessibility
1252     {
1253         isSettable = YES;
1254     }
1255     return isSettable;
1256 }
1257 
1258 - (void)accessibilitySetValueAttribute:(id)value
1259 {
1260 #ifdef JAVA_AX_DEBUG
1261     NSLog(@"Not yet implemented: %s\n", __FUNCTION__); // radr://3954018
1262 #endif
1263 }
1264 
1265 
1266 // Child elements that are visible (NSArray)
1267 - (NSArray *)accessibilityVisibleChildrenAttribute
1268 {
1269     JNIEnv *env = [ThreadUtilities getJNIEnv];
1270     NSArray *visibleChildren = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_VISIBLE_CHILDREN allowIgnored:NO];
1271     if ([visibleChildren count] <= 0) return nil;
1272     return visibleChildren;
1273 }
1274 
1275 - (BOOL)accessibilityIsVisibleChildrenAttributeSettable
1276 {
1277     return NO;
1278 }
1279 
1280 // Window containing current element (id)
1281 - (id)accessibilityWindowAttribute
1282 {
1283     return [self window];
1284 }
1285 
1286 - (BOOL)accessibilityIsWindowAttributeSettable
1287 {
1288     return NO;
1289 }
1290 
1291 
1292 // -- accessibility actions --
1293 - (NSArray *)accessibilityActionNames
1294 {
1295     JNIEnv *env = [ThreadUtilities getJNIEnv];
1296     return [[self getActions:env] allKeys];
1297 }
1298 
1299 - (NSString *)accessibilityActionDescription:(NSString *)action
1300 {
1301     AWT_ASSERT_APPKIT_THREAD;
1302 
1303     JNIEnv *env = [ThreadUtilities getJNIEnv];
1304     return [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] getDescription];
1305 }
1306 
1307 - (void)accessibilityPerformAction:(NSString *)action
1308 {
1309     AWT_ASSERT_APPKIT_THREAD;
1310 
1311     JNIEnv *env = [ThreadUtilities getJNIEnv];
1312     [(id <JavaAccessibilityAction>)[[self getActions:env] objectForKey:action] perform];
1313 }
1314 
1315 
1316 // -- misc accessibility --
1317 - (BOOL)accessibilityIsIgnored
1318 {
1319 #ifdef JAVA_AX_NO_IGNORES
1320     return NO;
1321 #else
1322     return [[self accessibilityRoleAttribute] isEqualToString:JavaAccessibilityIgnore];
1323 #endif /* JAVA_AX_NO_IGNORES */
1324 }
1325 
1326 - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
1327 {
1328     static JNF_CLASS_CACHE(jc_Container, "java/awt/Container");
1329     static JNF_STATIC_MEMBER_CACHE(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;");
1330 
1331     // Make it into java screen coords
1332     point.y = [[[[self view] window] screen] frame].size.height - point.y;
1333 
1334     jobject jparent = fComponent;
1335 
1336     id value = nil;
1337     if (JNFIsInstanceOf(env, jparent, &jc_Container)) {
1338         jobject jaccessible = JNFCallStaticObjectMethod(env, jm_accessibilityHitTest, jparent, (jfloat)point.x, (jfloat)point.y); // AWT_THREADING Safe (AWTRunLoop)
1339         if (jaccessible != NULL) {
1340             value = [JavaComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView];
1341             (*env)->DeleteLocalRef(env, jaccessible);
1342         }
1343     }
1344 
1345     if (value == nil) {
1346         value = self;
1347     }
1348 
1349     if ([value accessibilityIsIgnored]) {
1350         value = NSAccessibilityUnignoredAncestor(value);
1351     }
1352 
1353 #ifdef JAVA_AX_DEBUG
1354     NSLog(@"%s: %@", __FUNCTION__, value);
1355 #endif
1356     return value;
1357 }
1358 
1359 - (id)accessibilityFocusedUIElement
1360 {
1361     static JNF_STATIC_MEMBER_CACHE(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;");
1362 
1363     JNIEnv *env = [ThreadUtilities getJNIEnv];
1364     id value = nil;
1365 
1366     NSWindow* hostWindow = [[self->fView window] retain];
1367     jobject focused = JNFCallStaticObjectMethod(env, jm_getFocusOwner, fComponent); // AWT_THREADING Safe (AWTRunLoop)
1368     [hostWindow release];
1369 
1370     if (focused != NULL) {
1371         if (JNFIsInstanceOf(env, focused, &sjc_Accessible)) {
1372             value = [JavaComponentAccessibility createWithAccessible:focused withEnv:env withView:fView];
1373         }
1374         (*env)->DeleteLocalRef(env, focused);
1375     }
1376 
1377     if (value == nil) {
1378         value = self;
1379     }
1380 #ifdef JAVA_AX_DEBUG
1381     NSLog(@"%s: %@", __FUNCTION__, value);
1382 #endif
1383     return value;
1384 }
1385 
1386 @end
1387 
1388 /*
1389  * Class:     sun_lwawt_macosx_CAccessibility
1390  * Method:    focusChanged
1391  * Signature: ()V
1392  */
1393 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged
1394 (JNIEnv *env, jobject jthis)
1395 {
1396 JNF_COCOA_ENTER(env);
1397     [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) on:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO];
1398 JNF_COCOA_EXIT(env);
1399 }
1400 
1401 /*
1402  * Class:     sun_lwawt_macosx_CAccessible
1403  * Method:    valueChanged
1404  * Signature: (I)V
1405  */
1406 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged
1407 (JNIEnv *env, jclass jklass, jlong element)
1408 {
1409 JNF_COCOA_ENTER(env);
1410     [ThreadUtilities performOnMainThread:@selector(postValueChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1411 JNF_COCOA_EXIT(env);
1412 }
1413 
1414 /*
1415  * Class:     sun_lwawt_macosx_CAccessible
1416  * Method:    selectedTextChanged
1417  * Signature: (I)V
1418  */
1419 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedTextChanged
1420 (JNIEnv *env, jclass jklass, jlong element)
1421 {
1422 JNF_COCOA_ENTER(env);
1423     [ThreadUtilities performOnMainThread:@selector(postSelectedTextChanged)
1424                      on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1425                      withObject:nil
1426                      waitUntilDone:NO];
1427 JNF_COCOA_EXIT(env);
1428 }
1429 
1430 /*
1431  * Class:     sun_lwawt_macosx_CAccessible
1432  * Method:    selectionChanged
1433  * Signature: (I)V
1434  */
1435 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged
1436 (JNIEnv *env, jclass jklass, jlong element)
1437 {
1438 JNF_COCOA_ENTER(env);
1439     [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1440 JNF_COCOA_EXIT(env);
1441 }
1442 
1443 /*
1444  * Class:     sun_lwawt_macosx_CAccessible
1445  * Method:    menuOpened
1446  * Signature: (I)V
1447  */
1448 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuOpened
1449 (JNIEnv *env, jclass jklass, jlong element)
1450 {
1451 JNF_COCOA_ENTER(env);
1452     [ThreadUtilities performOnMainThread:@selector(postMenuOpened)
1453                      on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1454                      withObject:nil
1455                      waitUntilDone:NO];
1456 JNF_COCOA_EXIT(env);
1457 }
1458 
1459 /*
1460  * Class:     sun_lwawt_macosx_CAccessible
1461  * Method:    menuClosed
1462  * Signature: (I)V
1463  */
1464 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuClosed
1465 (JNIEnv *env, jclass jklass, jlong element)
1466 {
1467 JNF_COCOA_ENTER(env);
1468     [ThreadUtilities performOnMainThread:@selector(postMenuClosed)
1469                      on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1470                      withObject:nil
1471                      waitUntilDone:NO];
1472 JNF_COCOA_EXIT(env);
1473 }
1474 
1475 /*
1476  * Class:     sun_lwawt_macosx_CAccessible
1477  * Method:    menuItemSelected
1478  * Signature: (I)V
1479  */
1480 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuItemSelected
1481 (JNIEnv *env, jclass jklass, jlong element)
1482 {
1483 JNF_COCOA_ENTER(env);
1484     [ThreadUtilities performOnMainThread:@selector(postMenuItemSelected)
1485                      on:(JavaComponentAccessibility *)jlong_to_ptr(element)
1486                      withObject:nil
1487                      waitUntilDone:NO];
1488 JNF_COCOA_EXIT(env);
1489 }
1490 
1491 /*
1492  * Class:     sun_lwawt_macosx_CAccessible
1493  * Method:    unregisterFromCocoaAXSystem
1494  * Signature: (I)V
1495  */
1496 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem
1497 (JNIEnv *env, jclass jklass, jlong element)
1498 {
1499 JNF_COCOA_ENTER(env);
1500     [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) on:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO];
1501 JNF_COCOA_EXIT(env);
1502 }
1503 
1504 @implementation TabGroupAccessibility
1505 
1506 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
1507 {
1508     self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1509     if (self) {
1510         _numTabs = -1; //flag for uninitialized numTabs
1511     }
1512     return self;
1513 }
1514 
1515 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1516 {
1517     NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1518 
1519     [names addObject:NSAccessibilityTabsAttribute];
1520     [names addObject:NSAccessibilityContentsAttribute];
1521     [names addObject:NSAccessibilityValueAttribute];
1522 
1523     return names;
1524 }
1525 
1526 - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext
1527 {
1528     NSArray *tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1529 
1530     // Looking at the JTabbedPane sources, there is always one AccessibleSelection.
1531     jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent);
1532     if (selAccessible == NULL) return nil;
1533 
1534     // Go through the tabs and find selAccessible
1535     _numTabs = [tabs count];
1536     JavaComponentAccessibility *aTab;
1537     NSInteger i;
1538     for (i = 0; i < _numTabs; i++) {
1539         aTab = (JavaComponentAccessibility *)[tabs objectAtIndex:i];
1540         if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) {
1541             (*env)->DeleteLocalRef(env, selAccessible);
1542             return aTab;
1543         }
1544     }
1545     (*env)->DeleteLocalRef(env, selAccessible);
1546     return nil;
1547 }
1548 
1549 - (NSArray *)tabControlsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1550 {
1551     jobjectArray jtabsAndRoles = (jobjectArray)JNFCallStaticObjectMethod(env, jm_getChildrenAndRoles, fAccessible, fComponent, whichTabs, allowIgnored); // AWT_THREADING Safe (AWTRunLoop)
1552     if(jtabsAndRoles == NULL) return nil;
1553 
1554     jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles);
1555     if (arrayLen == 0) {
1556         (*env)->DeleteLocalRef(env, jtabsAndRoles);
1557         return nil;
1558     }
1559     NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)];
1560 
1561     // 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
1562     jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1.
1563     if (jtabJavaRole == NULL) {
1564         (*env)->DeleteLocalRef(env, jtabsAndRoles);
1565         return nil;
1566     }
1567     jobject jkey = JNFGetObjectField(env, jtabJavaRole, sjf_key);
1568     NSString *tabJavaRole = JNFJavaToNSString(env, jkey);
1569     (*env)->DeleteLocalRef(env, jkey);
1570 
1571     NSInteger i;
1572     NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly
1573     for(i = 0; i < arrayLen; i+=2) {
1574         jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i);
1575         JavaComponentAccessibility *tab = [[[TabGroupControlAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease];
1576         (*env)->DeleteLocalRef(env, jtab);
1577         [tabs addObject:tab];
1578         tabIndex++;
1579     }
1580     (*env)->DeleteLocalRef(env, jtabsAndRoles);
1581     return tabs;
1582 }
1583 
1584 - (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored
1585 {
1586     // Contents are the children of the selected tab.
1587     id currentTab = [self currentTabWithEnv:env withAxContext:axContext];
1588     if (currentTab == nil) return nil;
1589 
1590     NSArray *contents = [JavaComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored];
1591     if ([contents count] <= 0) return nil;
1592     return contents;
1593 }
1594 
1595 - (id) accessibilityTabsAttribute
1596 {
1597     JNIEnv *env = [ThreadUtilities getJNIEnv];
1598     jobject axContext = [self axContextWithEnv:env];
1599     id tabs = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1600     (*env)->DeleteLocalRef(env, axContext);
1601     return tabs;
1602 }
1603 
1604 - (BOOL)accessibilityIsTabsAttributeSettable
1605 {
1606     return NO; //cmcnote: not sure.
1607 }
1608 
1609 - (NSInteger)numTabs
1610 {
1611     if (_numTabs == -1) {
1612         _numTabs = [[self accessibilityTabsAttribute] count];
1613     }
1614     return _numTabs;
1615 }
1616 
1617 - (NSArray *) accessibilityContentsAttribute
1618 {
1619     JNIEnv *env = [ThreadUtilities getJNIEnv];
1620     jobject axContext = [self axContextWithEnv:env];
1621     NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO];
1622     (*env)->DeleteLocalRef(env, axContext);
1623     return cont;
1624 }
1625 
1626 - (BOOL)accessibilityIsContentsAttributeSettable
1627 {
1628     return NO;
1629 }
1630 
1631 // axValue is the currently selected tab
1632 -(id) accessibilityValueAttribute
1633 {
1634     JNIEnv *env = [ThreadUtilities getJNIEnv];
1635     jobject axContext = [self axContextWithEnv:env];
1636     id val = [self currentTabWithEnv:env withAxContext:axContext];
1637     (*env)->DeleteLocalRef(env, axContext);
1638     return val;
1639 }
1640 
1641 - (BOOL)accessibilityIsValueAttributeSettable
1642 {
1643     return YES;
1644 }
1645 
1646 - (void)accessibilitySetValueAttribute:(id)value //cmcnote: not certain this is ever actually called. investigate.
1647 {
1648     // set the current tab
1649     NSNumber *number = (NSNumber *)value;
1650     if (![number boolValue]) return;
1651 
1652     JNIEnv *env = [ThreadUtilities getJNIEnv];
1653     jobject axContext = [self axContextWithEnv:env];
1654     setAxContextSelection(env, axContext, fIndex, fComponent);
1655     (*env)->DeleteLocalRef(env, axContext);
1656 }
1657 
1658 - (NSArray *)accessibilityChildrenAttribute
1659 {
1660     //children = AXTabs + AXContents
1661     NSArray *tabs = [self accessibilityTabsAttribute];
1662     NSArray *contents = [self accessibilityContentsAttribute];
1663 
1664     NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]];
1665     [children addObjectsFromArray:tabs];
1666     [children addObjectsFromArray:contents];
1667 
1668     return (NSArray *)children;
1669 }
1670 
1671 // Without this optimization accessibilityChildrenAttribute is called in order to get the entire array of children.
1672 // See similar optimization in JavaComponentAccessibility. We have to extend the base implementation here, since
1673 // children of tabs are AXTabs + AXContents
1674 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount {
1675     NSArray *result = nil;
1676     if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1677         // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child
1678         JNIEnv *env = [ThreadUtilities getJNIEnv];
1679         jobject axContext = [self axContextWithEnv:env];
1680 
1681         //children = AXTabs + AXContents
1682         NSArray *children = [self tabControlsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs
1683         if ([children count] > 0) {
1684             result = children;
1685          } else {
1686             children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO];
1687             if ([children count] > 0) {
1688                 result = children;
1689             }
1690         }
1691         (*env)->DeleteLocalRef(env, axContext);
1692     } else {
1693         result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
1694     }
1695     return result;
1696 }
1697 
1698 @end
1699 
1700 
1701 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
1702 
1703 @implementation TabGroupControlAccessibility
1704 
1705 - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
1706 {
1707     self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole];
1708     if (self) {
1709         if (tabGroup != NULL) {
1710             fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroup);
1711         } else {
1712             fTabGroupAxContext = NULL;
1713         }
1714     }
1715     return self;
1716 }
1717 
1718 - (void)dealloc
1719 {
1720     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
1721 
1722     if (fTabGroupAxContext != NULL) {
1723         JNFDeleteWeakGlobalRef(env, fTabGroupAxContext);
1724         fTabGroupAxContext = NULL;
1725     }
1726 
1727     [super dealloc];
1728 }
1729 
1730 - (id)accessibilityValueAttribute
1731 {
1732     JNIEnv *env = [ThreadUtilities getJNIEnv];
1733     jobject axContext = [self axContextWithEnv:env];
1734     jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent);
1735 
1736     // Returns the current selection of the page tab list
1737     id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)];
1738 
1739     (*env)->DeleteLocalRef(env, selAccessible);
1740     (*env)->DeleteLocalRef(env, axContext);
1741     return val;
1742 }
1743 
1744 - (void)getActionsWithEnv:(JNIEnv *)env
1745 {
1746     TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent];
1747     [fActions setObject:action forKey:NSAccessibilityPressAction];
1748     [action release];
1749 }
1750 
1751 - (jobject)tabGroup
1752 {
1753     if (fTabGroupAxContext == NULL) {
1754         JNIEnv* env = [ThreadUtilities getJNIEnv];
1755         jobject tabGroupAxContext = [(JavaComponentAccessibility *)[self parent] axContextWithEnv:env];
1756         fTabGroupAxContext = JNFNewWeakGlobalRef(env, tabGroupAxContext);
1757         (*env)->DeleteLocalRef(env, tabGroupAxContext);
1758     }
1759     return fTabGroupAxContext;
1760 }
1761 
1762 @end
1763 
1764 
1765 @implementation ScrollAreaAccessibility
1766 
1767 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1768 {
1769     NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1770 
1771     [names addObject:NSAccessibilityHorizontalScrollBarAttribute];
1772     [names addObject:NSAccessibilityVerticalScrollBarAttribute];
1773     [names addObject:NSAccessibilityContentsAttribute];
1774 
1775     return names;
1776 }
1777 
1778 - (id)accessibilityHorizontalScrollBarAttribute
1779 {
1780     JNIEnv *env = [ThreadUtilities getJNIEnv];
1781 
1782     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1783     if ([children count] <= 0) return nil;
1784 
1785     // The scroll bars are in the children.
1786     JavaComponentAccessibility *aElement;
1787     NSEnumerator *enumerator = [children objectEnumerator];
1788     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1789         if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1790             jobject elementAxContext = [aElement axContextWithEnv:env];
1791             if (isHorizontal(env, elementAxContext, fComponent)) {
1792                 (*env)->DeleteLocalRef(env, elementAxContext);
1793                 return aElement;
1794             }
1795             (*env)->DeleteLocalRef(env, elementAxContext);
1796         }
1797     }
1798 
1799     return nil;
1800 }
1801 
1802 - (BOOL)accessibilityIsHorizontalScrollBarAttributeSettable
1803 {
1804     return NO;
1805 }
1806 
1807 - (id)accessibilityVerticalScrollBarAttribute
1808 {
1809     JNIEnv *env = [ThreadUtilities getJNIEnv];
1810 
1811     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1812     if ([children count] <= 0) return nil;
1813 
1814     // The scroll bars are in the children.
1815     NSEnumerator *enumerator = [children objectEnumerator];
1816     JavaComponentAccessibility *aElement;
1817     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1818         if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1819             jobject elementAxContext = [aElement axContextWithEnv:env];
1820             if (isVertical(env, elementAxContext, fComponent)) {
1821                 (*env)->DeleteLocalRef(env, elementAxContext);
1822                 return aElement;
1823             }
1824             (*env)->DeleteLocalRef(env, elementAxContext);
1825         }
1826     }
1827 
1828     return nil;
1829 }
1830 
1831 - (BOOL)accessibilityIsVerticalScrollBarAttributeSettable
1832 {
1833     return NO;
1834 }
1835 
1836 - (NSArray *)accessibilityContentsAttribute
1837 {
1838     JNIEnv *env = [ThreadUtilities getJNIEnv];
1839     NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES];
1840 
1841     if ([children count] <= 0) return nil;
1842     NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]];
1843 
1844     // The scroll bars are in the children. children less the scroll bars is the contents
1845     NSEnumerator *enumerator = [children objectEnumerator];
1846     JavaComponentAccessibility *aElement;
1847     while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) {
1848         if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) {
1849             // no scroll bars in contents
1850             [(NSMutableArray *)contents addObject:aElement];
1851         }
1852     }
1853 
1854     return contents;
1855 }
1856 
1857 - (BOOL)accessibilityIsContentsAttributeSettable
1858 {
1859     return NO;
1860 }
1861 
1862 @end
1863 
1864 // these constants are duplicated in CAccessibility.java
1865 #define JAVA_AX_ROWS (1)
1866 #define JAVA_AX_COLS (2)
1867 
1868 @implementation TableAccessibility
1869 
1870 - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
1871 {
1872     NSMutableArray *names = (NSMutableArray *)[super initializeAttributeNamesWithEnv:env];
1873 
1874     [names addObject:NSAccessibilityRowCountAttribute];
1875     [names addObject:NSAccessibilityColumnCountAttribute];
1876     return names;
1877 }
1878 
1879 - (id)getTableInfo:(jint)info {
1880     if (fAccessible == NULL) return 0;
1881 
1882     JNIEnv* env = [ThreadUtilities getJNIEnv];
1883     jint count = JNFCallStaticIntMethod(env, jm_getTableInfo, fAccessible,
1884                                         fComponent, info);
1885     NSNumber *index = [NSNumber numberWithInt:count];
1886     return index;
1887 }
1888 
1889 
1890 - (id)accessibilityRowCountAttribute {
1891     return [self getTableInfo:JAVA_AX_ROWS];
1892 }
1893 
1894 - (id)accessibilityColumnCountAttribute {
1895     return [self getTableInfo:JAVA_AX_COLS];
1896 }
1897 @end
1898 
1899 /*
1900  * Returns Object.equals for the two items
1901  * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock
1902  * and try to pass a component so the event happens on the correct thread.
1903  */
1904 static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object");
1905 static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component)
1906 {
1907     static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z");
1908 
1909     if ((a == NULL) && (b == NULL)) return YES;
1910     if ((a == NULL) || (b == NULL)) return NO;
1911 
1912     if (pthread_main_np() != 0) {
1913         // If we are on the AppKit thread
1914         static JNF_CLASS_CACHE(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
1915         static JNF_STATIC_MEMBER_CACHE(jm_doEquals, sjc_LWCToolkit, "doEquals", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z");
1916         return JNFCallStaticBooleanMethod(env, jm_doEquals, a, b, component); // AWT_THREADING Safe (AWTRunLoopMode)
1917     }
1918 
1919     return JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit)
1920 }