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