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