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