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