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