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