1 /* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import "JavaAccessibilityUtilities.h" 27 28 #import <AppKit/AppKit.h> 29 #import <JavaNativeFoundation/JavaNativeFoundation.h> 30 31 32 static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute); 33 static void JavaAccessibilityLogError(NSString *message); 34 static void _JavaAccessibilityRaiseException(NSString *reason, SInt32 errorCode); 35 static NSString *AttributeWithoutAXPrefix(NSString *attribute); 36 static SEL JavaAccessibilityAttributeGetter(NSString *attribute); 37 static SEL JavaAccessibilityAttributeSettableTester(NSString *attribute); 38 static SEL JavaAccessibilityAttributeSetter(NSString *attribute); 39 40 NSString *const JavaAccessibilityIgnore = @"JavaAxIgnore"; 41 42 NSMutableDictionary *sRoles = nil; 43 void initializeRoles(); 44 45 // Unique 46 static JNF_CLASS_CACHE(sjc_AccessibleState, "javax/accessibility/AccessibleState"); 47 48 // Duplicate 49 JNF_CLASS_CACHE(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility"); 50 JNF_CLASS_CACHE(sjc_AccessibleComponent, "javax/accessibility/AccessibleComponent"); 51 JNF_CLASS_CACHE(sjc_AccessibleContext, "javax/accessibility/AccessibleContext"); 52 JNF_CLASS_CACHE(sjc_Accessible, "javax/accessibility/Accessible"); 53 JNF_CLASS_CACHE(sjc_AccessibleRole, "javax/accessibility/AccessibleRole"); 54 JNF_CLASS_CACHE(sjc_Point, "java/awt/Point"); 55 JNF_CLASS_CACHE(sjc_AccessibleText, "javax/accessibility/AccessibleText"); 56 57 JNF_MEMBER_CACHE(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;"); 58 JNF_MEMBER_CACHE(sjf_X, sjc_Point, "x", "I"); 59 JNF_MEMBER_CACHE(sjf_Y, sjc_Point, "y", "I"); 60 61 NSSize getAxComponentSize(JNIEnv *env, jobject axComponent, jobject component) 62 { 63 static JNF_CLASS_CACHE(jc_Dimension, "java/awt/Dimension"); 64 static JNF_MEMBER_CACHE(jf_width, jc_Dimension, "width", "I"); 65 static JNF_MEMBER_CACHE(jf_height, jc_Dimension, "height", "I"); 66 static JNF_STATIC_MEMBER_CACHE(jm_getSize, sjc_CAccessibility, "getSize", "(Ljavax/accessibility/AccessibleComponent;Ljava/awt/Component;)Ljava/awt/Dimension;"); 67 68 jobject dimension = JNFCallStaticObjectMethod(env, jm_getSize, axComponent, component); // AWT_THREADING Safe (AWTRunLoopMode) 69 70 if (dimension == NULL) return NSZeroSize; 71 return NSMakeSize(JNFGetIntField(env, dimension, jf_width), JNFGetIntField(env, dimension, jf_height)); 72 } 73 74 NSString *getJavaRole(JNIEnv *env, jobject axComponent, jobject component) 75 { 76 static JNF_STATIC_MEMBER_CACHE(sjm_getAccessibleRole, sjc_CAccessibility, "getAccessibleRole", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;"); 77 jobject axRole = JNFCallStaticObjectMethod(env, sjm_getAccessibleRole, axComponent, component); // AWT_THREADING Safe (AWTRunLoopMode) 78 if (axRole == NULL) return @"unknown"; 79 80 NSString* str = JNFJavaToNSString(env, axRole); 81 (*env)->DeleteLocalRef(env, axRole); 82 return str; 83 } 84 85 jobject getAxSelection(JNIEnv *env, jobject axContext, jobject component) 86 { 87 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleSelection, sjc_CAccessibility, "getAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleSelection;"); 88 return JNFCallStaticObjectMethod(env, jm_getAccessibleSelection, axContext, component); // AWT_THREADING Safe (AWTRunLoopMode) 89 } 90 91 jobject getAxContextSelection(JNIEnv *env, jobject axContext, jint index, jobject component) 92 { 93 static JNF_STATIC_MEMBER_CACHE(jm_ax_getAccessibleSelection, sjc_CAccessibility, "ax_getAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;ILjava/awt/Component;)Ljavax/accessibility/Accessible;"); 94 return JNFCallStaticObjectMethod(env, jm_ax_getAccessibleSelection, axContext, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 95 } 96 97 void setAxContextSelection(JNIEnv *env, jobject axContext, jint index, jobject component) 98 { 99 static JNF_STATIC_MEMBER_CACHE(jm_addAccessibleSelection, sjc_CAccessibility, "addAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;ILjava/awt/Component;)V"); 100 JNFCallStaticVoidMethod(env, jm_addAccessibleSelection, axContext, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 101 } 102 103 jobject getAxContext(JNIEnv *env, jobject accessible, jobject component) 104 { 105 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleContext, sjc_CAccessibility, "getAccessibleContext", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleContext;"); 106 return JNFCallStaticObjectMethod(env, jm_getAccessibleContext, accessible, component); // AWT_THREADING Safe (AWTRunLoopMode) 107 } 108 109 BOOL isChildSelected(JNIEnv *env, jobject accessible, jint index, jobject component) 110 { 111 static JNF_STATIC_MEMBER_CACHE(jm_isAccessibleChildSelected, sjc_CAccessibility, "isAccessibleChildSelected", "(Ljavax/accessibility/Accessible;ILjava/awt/Component;)Z"); 112 return JNFCallStaticBooleanMethod(env, jm_isAccessibleChildSelected, accessible, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 113 } 114 115 jobject getAxStateSet(JNIEnv *env, jobject axContext, jobject component) 116 { 117 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleStateSet, sjc_CAccessibility, "getAccessibleStateSet", "(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleStateSet;"); 118 return JNFCallStaticObjectMethod(env, jm_getAccessibleStateSet, axContext, component); // AWT_THREADING Safe (AWTRunLoopMode) 119 } 120 121 BOOL containsAxState(JNIEnv *env, jobject axContext, jobject axState, jobject component) 122 { 123 static JNF_STATIC_MEMBER_CACHE(jm_contains, sjc_CAccessibility, "contains", "(Ljavax/accessibility/AccessibleContext;Ljavax/accessibility/AccessibleState;Ljava/awt/Component;)Z"); 124 return JNFCallStaticBooleanMethod(env, jm_contains, axContext, axState, component); // AWT_THREADING Safe (AWTRunLoopMode) 125 } 126 127 BOOL isVertical(JNIEnv *env, jobject axContext, jobject component) 128 { 129 static JNF_STATIC_MEMBER_CACHE(jm_VERTICAL, sjc_AccessibleState, "VERTICAL", "Ljavax/accessibility/AccessibleState;"); 130 jobject axVertState = JNFGetStaticObjectField(env, jm_VERTICAL); 131 BOOL vertical = containsAxState(env, axContext, axVertState, component); 132 (*env)->DeleteLocalRef(env, axVertState); 133 return vertical; 134 } 135 136 BOOL isHorizontal(JNIEnv *env, jobject axContext, jobject component) 137 { 138 static JNF_STATIC_MEMBER_CACHE(jm_HORIZONTAL, sjc_AccessibleState, "HORIZONTAL", "Ljavax/accessibility/AccessibleState;"); 139 jobject axHorizState = JNFGetStaticObjectField(env, jm_HORIZONTAL); 140 BOOL horizontal = containsAxState(env, axContext, axHorizState, component); 141 (*env)->DeleteLocalRef(env, axHorizState); 142 return horizontal; 143 } 144 145 BOOL isShowing(JNIEnv *env, jobject axContext, jobject component) 146 { 147 static JNF_STATIC_MEMBER_CACHE(jm_SHOWING, sjc_AccessibleState, "SHOWING", "Ljavax/accessibility/AccessibleState;"); 148 jobject axVisibleState = JNFGetStaticObjectField(env, jm_SHOWING); 149 BOOL showing = containsAxState(env, axContext, axVisibleState, component); 150 (*env)->DeleteLocalRef(env, axVisibleState); 151 return showing; 152 } 153 154 BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component) 155 { 156 static JNF_STATIC_MEMBER_CACHE( jm_SELECTABLE, 157 sjc_AccessibleState, 158 "SELECTABLE", 159 "Ljavax/accessibility/AccessibleState;" ); 160 jobject axSelectableState = JNFGetStaticObjectField(env, jm_SELECTABLE); 161 BOOL selectable = containsAxState(env, axContext, axSelectableState, component); 162 (*env)->DeleteLocalRef(env, axSelectableState); 163 return selectable; 164 } 165 166 NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component) 167 { 168 static JNF_STATIC_MEMBER_CACHE(jm_getLocationOnScreen, sjc_CAccessibility, "getLocationOnScreen", "(Ljavax/accessibility/AccessibleComponent;Ljava/awt/Component;)Ljava/awt/Point;"); 169 jobject jpoint = JNFCallStaticObjectMethod(env, jm_getLocationOnScreen, axComponent, component); // AWT_THREADING Safe (AWTRunLoopMode) 170 if (jpoint == NULL) return NSZeroPoint; 171 return NSMakePoint(JNFGetIntField(env, jpoint, sjf_X), JNFGetIntField(env, jpoint, sjf_Y)); 172 } 173 174 jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component) 175 { 176 static JNF_STATIC_MEMBER_CACHE(jm_getCharCount, sjc_CAccessibility, "getCharCount", "(Ljavax/accessibility/AccessibleText;Ljava/awt/Component;)I"); 177 return JNFCallStaticIntMethod(env, jm_getCharCount, axText, component); // AWT_THREADING Safe (AWTRunLoopMode) 178 } 179 180 // The following JavaAccessibility methods are copied from the corresponding 181 // NSAccessibility methods in NSAccessibility.m. 182 // 183 // They implement a key-value-like coding scheme to transform messages like 184 // [self accessibilityAttributeValue:NSAccessibilityEnabledAttribute] 185 // into calls on to specific methods like 186 // [self accessibilityEnabledAttribute]. 187 188 static NSString *AttributeWithoutAXPrefix(NSString *attribute) 189 { 190 return [attribute hasPrefix:@"AX"] ? [attribute substringFromIndex:2] : attribute; 191 } 192 193 static SEL JavaAccessibilityAttributeGetter(NSString *attribute) 194 { 195 return NSSelectorFromString([NSString stringWithFormat:@"accessibility%@Attribute", AttributeWithoutAXPrefix(attribute)]); 196 } 197 198 static SEL JavaAccessibilityAttributeSettableTester(NSString *attribute) 199 { 200 return NSSelectorFromString([NSString stringWithFormat:@"accessibilityIs%@AttributeSettable", AttributeWithoutAXPrefix(attribute)]); 201 } 202 203 static SEL JavaAccessibilityAttributeSetter(NSString *attribute) 204 { 205 return NSSelectorFromString([NSString stringWithFormat:@"accessibilitySet%@Attribute:", AttributeWithoutAXPrefix(attribute)]); 206 } 207 208 id JavaAccessibilityAttributeValue(id element, NSString *attribute) 209 { 210 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return nil; 211 212 SEL getter = JavaAccessibilityAttributeGetter(attribute); 213 #ifdef JAVA_AX_DEBUG_PARMS 214 if (![element respondsToSelector:getter]) { 215 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 216 return nil; 217 } 218 #endif 219 220 return [element performSelector:getter]; 221 } 222 223 BOOL JavaAccessibilityIsAttributeSettable(id element, NSString *attribute) 224 { 225 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return NO; 226 227 SEL tester = JavaAccessibilityAttributeSettableTester(attribute); 228 #ifdef JAVA_AX_DEBUG_PARMS 229 if (![element respondsToSelector:tester]) { 230 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 231 return NO; 232 } 233 #endif 234 235 return [element performSelector:tester] != nil; 236 } 237 238 void JavaAccessibilitySetAttributeValue(id element, NSString *attribute ,id value) 239 { 240 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return; 241 242 SEL setter = JavaAccessibilityAttributeSetter(attribute); 243 if (![element accessibilityIsAttributeSettable:attribute]) return; 244 245 #ifdef JAVA_AX_DEBUG_PARMS 246 if (![element respondsToSelector:setter]) { 247 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 248 return; 249 } 250 #endif 251 252 [element performSelector:setter withObject:value]; 253 } 254 255 static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute) 256 { 257 return [[element accessibilityAttributeNames] indexOfObject:attribute] != NSNotFound; 258 } 259 260 /* 261 * Class: sun_lwawt_macosx_CAccessibility 262 * Method: roleKey 263 * Signature: (Ljavax/accessibility/AccessibleRole;)Ljava/lang/String; 264 */ 265 JNIEXPORT jstring JNICALL Java_sun_lwawt_macosx_CAccessibility_roleKey 266 (JNIEnv *env, jclass clz, jobject axRole) 267 { 268 return JNFGetObjectField(env, axRole, sjf_key); 269 } 270 271 272 // errors from NSAccessibilityErrors 273 void JavaAccessibilityRaiseSetAttributeToIllegalTypeException(const char *functionName, id element, NSString *attribute, id value) 274 { 275 NSString *reason = [NSString stringWithFormat:@"%s: Attempt set \"%@\" attribute to illegal type of value (%@:%@) for element: %@", functionName, attribute, [value class], value, element]; 276 _JavaAccessibilityRaiseException(reason, kAXErrorIllegalArgument); 277 } 278 279 void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionName, id element, NSString *attribute) 280 { 281 NSString *reason = [NSString stringWithFormat:@"%s: \"%@\" attribute unimplemented by element: %@", functionName, attribute, element]; 282 _JavaAccessibilityRaiseException(reason, kAXErrorFailure); 283 } 284 285 void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter) 286 { 287 NSString *reason = [NSString stringWithFormat:@"%s: \"%@\" parameterized attribute passed illegal type of parameter (%@:%@) for element: %@", functionName, attribute, [parameter class], parameter, element]; 288 _JavaAccessibilityRaiseException(reason, kAXErrorIllegalArgument); 289 } 290 291 static void _JavaAccessibilityRaiseException(NSString *reason, SInt32 errorCode) 292 { 293 JavaAccessibilityLogError(reason); 294 [[NSException exceptionWithName:NSAccessibilityException reason:reason userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:errorCode], NSAccessibilityErrorCodeExceptionInfo, nil]] raise]; 295 } 296 297 static void JavaAccessibilityLogError(NSString *message) 298 { 299 NSLog(@"!!! %@", message); 300 } 301 302 // end appKit copies 303 304 /* 305 To get the roles below, verify the perl has table below called macRoleCodes is correct. 306 Then copy the perl code into a perl script called makeAxTables.pl (make 307 sure to chmod +x makeAxTables.pl). Then run the perl script like this: 308 309 ./makeAxTables.pl /Builds/jdk1_4_1/ 310 311 It will then write the void initializeRoles() method below to stdout. 312 313 Any new AccessibleRole items that aren't in the perl hash table will be written out as follows: 314 // Unknown AccessibleRole: <role> 315 316 Add these unknowns to the perl hash table and re-run the script, and use the new generated table. 317 */ 318 319 // NOTE: Don't modify this directly. It is machine generated. See below 320 void initializeRoles() 321 { 322 sRoles = [[NSMutableDictionary alloc] initWithCapacity:56]; 323 324 [sRoles setObject:JavaAccessibilityIgnore forKey:@"alert"]; 325 [sRoles setObject:NSAccessibilityGroupRole forKey:@"awtcomponent"]; 326 [sRoles setObject:NSAccessibilityGroupRole forKey:@"canvas"]; 327 [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"checkbox"]; 328 [sRoles setObject:JavaAccessibilityIgnore forKey:@"colorchooser"]; 329 [sRoles setObject:NSAccessibilityColumnRole forKey:@"columnheader"]; 330 [sRoles setObject:NSAccessibilityComboBoxRole forKey:@"combobox"]; 331 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"dateeditor"]; 332 [sRoles setObject:NSAccessibilityImageRole forKey:@"desktopicon"]; 333 [sRoles setObject:JavaAccessibilityIgnore forKey:@"desktoppane"]; 334 [sRoles setObject:JavaAccessibilityIgnore forKey:@"dialog"]; 335 [sRoles setObject:JavaAccessibilityIgnore forKey:@"directorypane"]; 336 [sRoles setObject:JavaAccessibilityIgnore forKey:@"filechooser"]; 337 [sRoles setObject:JavaAccessibilityIgnore forKey:@"filler"]; 338 [sRoles setObject:JavaAccessibilityIgnore forKey:@"fontchooser"]; 339 [sRoles setObject:JavaAccessibilityIgnore forKey:@"frame"]; 340 [sRoles setObject:JavaAccessibilityIgnore forKey:@"glasspane"]; 341 [sRoles setObject:NSAccessibilityGroupRole forKey:@"groupbox"]; 342 [sRoles setObject:NSAccessibilityStaticTextRole forKey:@"hyperlink"]; //maybe a group? 343 [sRoles setObject:NSAccessibilityImageRole forKey:@"icon"]; 344 [sRoles setObject:NSAccessibilityGroupRole forKey:@"internalframe"]; 345 [sRoles setObject:NSAccessibilityStaticTextRole forKey:@"label"]; 346 [sRoles setObject:JavaAccessibilityIgnore forKey:@"layeredpane"]; 347 [sRoles setObject:NSAccessibilityListRole forKey:@"list"]; // maybe a group? AccessibleRole.java says a list is: "An object that presents a list of objects to the user and allows the user to select one or more of them." 348 [sRoles setObject:NSAccessibilityListRole forKey:@"listitem"]; 349 [sRoles setObject:NSAccessibilityMenuRole forKey:@"menu"]; 350 [sRoles setObject:NSAccessibilityMenuBarRole forKey:@"menubar"]; 351 [sRoles setObject:NSAccessibilityMenuItemRole forKey:@"menuitem"]; 352 [sRoles setObject:JavaAccessibilityIgnore forKey:@"optionpane"]; 353 [sRoles setObject:NSAccessibilityRadioButtonRole forKey:@"pagetab"]; // cmcnote: cocoa tabs are radio buttons - one selected button out of a group of options 354 [sRoles setObject:NSAccessibilityTabGroupRole forKey:@"pagetablist"]; 355 [sRoles setObject:JavaAccessibilityIgnore forKey:@"panel"]; 356 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"passwordtext"]; 357 [sRoles setObject:NSAccessibilityPopUpButtonRole forKey:@"popupmenu"]; 358 [sRoles setObject:NSAccessibilityProgressIndicatorRole forKey:@"progressbar"]; 359 [sRoles setObject:NSAccessibilityButtonRole forKey:@"pushbutton"]; 360 [sRoles setObject:NSAccessibilityRadioButtonRole forKey:@"radiobutton"]; 361 [sRoles setObject:JavaAccessibilityIgnore forKey:@"rootpane"]; 362 [sRoles setObject:NSAccessibilityRowRole forKey:@"rowheader"]; 363 [sRoles setObject:NSAccessibilityScrollBarRole forKey:@"scrollbar"]; 364 [sRoles setObject:NSAccessibilityScrollAreaRole forKey:@"scrollpane"]; 365 [sRoles setObject:JavaAccessibilityIgnore forKey:@"separator"]; 366 [sRoles setObject:NSAccessibilitySliderRole forKey:@"slider"]; 367 [sRoles setObject:NSAccessibilityIncrementorRole forKey:@"spinbox"]; 368 [sRoles setObject:NSAccessibilitySplitGroupRole forKey:@"splitpane"]; 369 [sRoles setObject:NSAccessibilityValueIndicatorRole forKey:@"statusbar"]; 370 [sRoles setObject:NSAccessibilityGroupRole forKey:@"swingcomponent"]; 371 [sRoles setObject:NSAccessibilityTableRole forKey:@"table"]; 372 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"text"]; 373 [sRoles setObject:NSAccessibilityTextAreaRole forKey:@"textarea"]; // supports top/bottom of document notifications: CAccessability.getAccessibleRole() 374 [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"]; 375 [sRoles setObject:NSAccessibilityToolbarRole forKey:@"toolbar"]; 376 [sRoles setObject:JavaAccessibilityIgnore forKey:@"tooltip"]; 377 [sRoles setObject:NSAccessibilityBrowserRole forKey:@"tree"]; 378 [sRoles setObject:NSAccessibilityUnknownRole forKey:@"unknown"]; 379 [sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"]; 380 [sRoles setObject:JavaAccessibilityIgnore forKey:@"window"]; 381 }