1 /* 2 * Copyright (c) 2011, 2012, 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 return JNFJavaToNSString(env, axRole); 81 } 82 83 jobject getAxSelection(JNIEnv *env, jobject axContext, jobject component) 84 { 85 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleSelection, sjc_CAccessibility, "getAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleSelection;"); 86 return JNFCallStaticObjectMethod(env, jm_getAccessibleSelection, axContext, component); // AWT_THREADING Safe (AWTRunLoopMode) 87 } 88 89 jobject getAxContextSelection(JNIEnv *env, jobject axContext, jint index, jobject component) 90 { 91 static JNF_STATIC_MEMBER_CACHE(jm_ax_getAccessibleSelection, sjc_CAccessibility, "ax_getAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;ILjava/awt/Component;)Ljavax/accessibility/Accessible;"); 92 return JNFCallStaticObjectMethod(env, jm_ax_getAccessibleSelection, axContext, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 93 } 94 95 void setAxContextSelection(JNIEnv *env, jobject axContext, jint index, jobject component) 96 { 97 static JNF_STATIC_MEMBER_CACHE(jm_addAccessibleSelection, sjc_CAccessibility, "addAccessibleSelection", "(Ljavax/accessibility/AccessibleContext;ILjava/awt/Component;)V"); 98 JNFCallStaticVoidMethod(env, jm_addAccessibleSelection, axContext, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 99 } 100 101 jobject getAxContext(JNIEnv *env, jobject accessible, jobject component) 102 { 103 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleContext, sjc_CAccessibility, "getAccessibleContext", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleContext;"); 104 return JNFCallStaticObjectMethod(env, jm_getAccessibleContext, accessible, component); // AWT_THREADING Safe (AWTRunLoopMode) 105 } 106 107 BOOL isChildSelected(JNIEnv *env, jobject accessible, jint index, jobject component) 108 { 109 static JNF_STATIC_MEMBER_CACHE(jm_isAccessibleChildSelected, sjc_CAccessibility, "isAccessibleChildSelected", "(Ljavax/accessibility/Accessible;ILjava/awt/Component;)Z"); 110 return JNFCallStaticBooleanMethod(env, jm_isAccessibleChildSelected, accessible, index, component); // AWT_THREADING Safe (AWTRunLoopMode) 111 } 112 113 jobject getAxStateSet(JNIEnv *env, jobject axContext, jobject component) 114 { 115 static JNF_STATIC_MEMBER_CACHE(jm_getAccessibleStateSet, sjc_CAccessibility, "getAccessibleStateSet", "(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleStateSet;"); 116 return JNFCallStaticObjectMethod(env, jm_getAccessibleStateSet, axContext, component); // AWT_THREADING Safe (AWTRunLoopMode) 117 } 118 119 BOOL containsAxState(JNIEnv *env, jobject axContext, jobject axState, jobject component) 120 { 121 static JNF_STATIC_MEMBER_CACHE(jm_contains, sjc_CAccessibility, "contains", "(Ljavax/accessibility/AccessibleContext;Ljavax/accessibility/AccessibleState;Ljava/awt/Component;)Z"); 122 return JNFCallStaticBooleanMethod(env, jm_contains, axContext, axState, component); // AWT_THREADING Safe (AWTRunLoopMode) 123 } 124 125 BOOL isVertical(JNIEnv *env, jobject axContext, jobject component) 126 { 127 static JNF_STATIC_MEMBER_CACHE(jm_VERTICAL, sjc_AccessibleState, "VERTICAL", "Ljavax/accessibility/AccessibleState;"); 128 jobject axVertState = JNFGetStaticObjectField(env, jm_VERTICAL); 129 return containsAxState(env, axContext, axVertState, component); 130 } 131 132 BOOL isHorizontal(JNIEnv *env, jobject axContext, jobject component) 133 { 134 static JNF_STATIC_MEMBER_CACHE(jm_HORIZONTAL, sjc_AccessibleState, "HORIZONTAL", "Ljavax/accessibility/AccessibleState;"); 135 jobject axHorizState = JNFGetStaticObjectField(env, jm_HORIZONTAL); 136 return containsAxState(env, axContext, axHorizState, component); 137 } 138 139 BOOL isShowing(JNIEnv *env, jobject axContext, jobject component) 140 { 141 static JNF_STATIC_MEMBER_CACHE(jm_SHOWING, sjc_AccessibleState, "SHOWING", "Ljavax/accessibility/AccessibleState;"); 142 jobject axVisibleState = JNFGetStaticObjectField(env, jm_SHOWING); 143 return containsAxState(env, axContext, axVisibleState, component); 144 } 145 146 NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component) 147 { 148 static JNF_STATIC_MEMBER_CACHE(jm_getLocationOnScreen, sjc_CAccessibility, "getLocationOnScreen", "(Ljavax/accessibility/AccessibleComponent;Ljava/awt/Component;)Ljava/awt/Point;"); 149 jobject jpoint = JNFCallStaticObjectMethod(env, jm_getLocationOnScreen, axComponent, component); // AWT_THREADING Safe (AWTRunLoopMode) 150 if (jpoint == NULL) return NSZeroPoint; 151 return NSMakePoint(JNFGetIntField(env, jpoint, sjf_X), JNFGetIntField(env, jpoint, sjf_Y)); 152 } 153 154 jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component) 155 { 156 static JNF_STATIC_MEMBER_CACHE(jm_getCharCount, sjc_CAccessibility, "getCharCount", "(Ljavax/accessibility/AccessibleText;Ljava/awt/Component;)I"); 157 return JNFCallStaticIntMethod(env, jm_getCharCount, axText, component); // AWT_THREADING Safe (AWTRunLoopMode) 158 } 159 160 // The following JavaAccessibility methods are copied from the corresponding 161 // NSAccessibility methods in NSAccessibility.m. 162 // 163 // They implement a key-value-like coding scheme to transform messages like 164 // [self accessibilityAttributeValue:NSAccessibilityEnabledAttribute] 165 // into calls on to specific methods like 166 // [self accessibilityEnabledAttribute]. 167 168 static NSString *AttributeWithoutAXPrefix(NSString *attribute) 169 { 170 return [attribute hasPrefix:@"AX"] ? [attribute substringFromIndex:2] : attribute; 171 } 172 173 static SEL JavaAccessibilityAttributeGetter(NSString *attribute) 174 { 175 return NSSelectorFromString([NSString stringWithFormat:@"accessibility%@Attribute", AttributeWithoutAXPrefix(attribute)]); 176 } 177 178 static SEL JavaAccessibilityAttributeSettableTester(NSString *attribute) 179 { 180 return NSSelectorFromString([NSString stringWithFormat:@"accessibilityIs%@AttributeSettable", AttributeWithoutAXPrefix(attribute)]); 181 } 182 183 static SEL JavaAccessibilityAttributeSetter(NSString *attribute) 184 { 185 return NSSelectorFromString([NSString stringWithFormat:@"accessibilitySet%@Attribute:", AttributeWithoutAXPrefix(attribute)]); 186 } 187 188 id JavaAccessibilityAttributeValue(id element, NSString *attribute) 189 { 190 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return nil; 191 192 SEL getter = JavaAccessibilityAttributeGetter(attribute); 193 #ifdef JAVA_AX_DEBUG_PARMS 194 if (![element respondsToSelector:getter]) { 195 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 196 return nil; 197 } 198 #endif 199 200 return [element performSelector:getter]; 201 } 202 203 BOOL JavaAccessibilityIsAttributeSettable(id element, NSString *attribute) 204 { 205 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return NO; 206 207 SEL tester = JavaAccessibilityAttributeSettableTester(attribute); 208 #ifdef JAVA_AX_DEBUG_PARMS 209 if (![element respondsToSelector:tester]) { 210 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 211 return NO; 212 } 213 #endif 214 215 return [element performSelector:tester] != nil; 216 } 217 218 void JavaAccessibilitySetAttributeValue(id element, NSString *attribute ,id value) 219 { 220 if (!JavaAccessibilityIsSupportedAttribute(element, attribute)) return; 221 222 SEL setter = JavaAccessibilityAttributeSetter(attribute); 223 if (![element accessibilityIsAttributeSettable:attribute]) return; 224 225 #ifdef JAVA_AX_DEBUG_PARMS 226 if (![element respondsToSelector:setter]) { 227 JavaAccessibilityRaiseUnimplementedAttributeException(__FUNCTION__, element, attribute); 228 return; 229 } 230 #endif 231 232 [element performSelector:setter withObject:value]; 233 } 234 235 static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute) 236 { 237 return [[element accessibilityAttributeNames] indexOfObject:attribute] != NSNotFound; 238 } 239 240 /* 241 * Class: sun_lwawt_macosx_CAccessibility 242 * Method: roleKey 243 * Signature: (Ljavax/accessibility/AccessibleRole;)Ljava/lang/String; 244 */ 245 JNIEXPORT jstring JNICALL Java_sun_lwawt_macosx_CAccessibility_roleKey 246 (JNIEnv *env, jclass clz, jobject axRole) 247 { 248 return JNFGetObjectField(env, axRole, sjf_key); 249 } 250 251 252 // errors from NSAccessibilityErrors 253 void JavaAccessibilityRaiseSetAttributeToIllegalTypeException(const char *functionName, id element, NSString *attribute, id value) 254 { 255 NSString *reason = [NSString stringWithFormat:@"%s: Attempt set \"%@\" attribute to illegal type of value (%@:%@) for element: %@", functionName, attribute, [value class], value, element]; 256 _JavaAccessibilityRaiseException(reason, kAXErrorIllegalArgument); 257 } 258 259 void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionName, id element, NSString *attribute) 260 { 261 NSString *reason = [NSString stringWithFormat:@"%s: \"%@\" attribute unimplemented by element: %@", functionName, attribute, element]; 262 _JavaAccessibilityRaiseException(reason, kAXErrorFailure); 263 } 264 265 void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter) 266 { 267 NSString *reason = [NSString stringWithFormat:@"%s: \"%@\" parameterized attribute passed illegal type of parameter (%@:%@) for element: %@", functionName, attribute, [parameter class], parameter, element]; 268 _JavaAccessibilityRaiseException(reason, kAXErrorIllegalArgument); 269 } 270 271 static void _JavaAccessibilityRaiseException(NSString *reason, SInt32 errorCode) 272 { 273 JavaAccessibilityLogError(reason); 274 [[NSException exceptionWithName:NSAccessibilityException reason:reason userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:errorCode], NSAccessibilityErrorCodeExceptionInfo, nil]] raise]; 275 } 276 277 static void JavaAccessibilityLogError(NSString *message) 278 { 279 NSLog(@"!!! %@", message); 280 } 281 282 // end appKit copies 283 284 /* 285 To get the roles below, verify the perl has table below called macRoleCodes is correct. 286 Then copy the perl code into a perl script called makeAxTables.pl (make 287 sure to chmod +x makeAxTables.pl). Then run the perl script like this: 288 289 ./makeAxTables.pl /Builds/jdk1_4_1/ 290 291 It will then write the void initializeRoles() method below to stdout. 292 293 Any new AccessibleRole items that aren't in the perl hash table will be written out as follows: 294 // Unknown AccessibleRole: <role> 295 296 Add these unknowns to the perl hash table and re-run the script, and use the new generated table. 297 */ 298 299 // NOTE: Don't modify this directly. It is machine generated. See below 300 void initializeRoles() 301 { 302 sRoles = [[NSMutableDictionary alloc] initWithCapacity:56]; 303 304 [sRoles setObject:JavaAccessibilityIgnore forKey:@"alert"]; 305 [sRoles setObject:NSAccessibilityGroupRole forKey:@"awtcomponent"]; 306 [sRoles setObject:NSAccessibilityGroupRole forKey:@"canvas"]; 307 [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"checkbox"]; 308 [sRoles setObject:JavaAccessibilityIgnore forKey:@"colorchooser"]; 309 [sRoles setObject:NSAccessibilityColumnRole forKey:@"columnheader"]; 310 [sRoles setObject:NSAccessibilityComboBoxRole forKey:@"combobox"]; 311 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"dateeditor"]; 312 [sRoles setObject:NSAccessibilityImageRole forKey:@"desktopicon"]; 313 [sRoles setObject:JavaAccessibilityIgnore forKey:@"desktoppane"]; 314 [sRoles setObject:JavaAccessibilityIgnore forKey:@"dialog"]; 315 [sRoles setObject:JavaAccessibilityIgnore forKey:@"directorypane"]; 316 [sRoles setObject:JavaAccessibilityIgnore forKey:@"filechooser"]; 317 [sRoles setObject:JavaAccessibilityIgnore forKey:@"filler"]; 318 [sRoles setObject:JavaAccessibilityIgnore forKey:@"fontchooser"]; 319 [sRoles setObject:JavaAccessibilityIgnore forKey:@"frame"]; 320 [sRoles setObject:JavaAccessibilityIgnore forKey:@"glasspane"]; 321 [sRoles setObject:NSAccessibilityGroupRole forKey:@"groupbox"]; 322 [sRoles setObject:NSAccessibilityStaticTextRole forKey:@"hyperlink"]; //maybe a group? 323 [sRoles setObject:NSAccessibilityImageRole forKey:@"icon"]; 324 [sRoles setObject:NSAccessibilityGroupRole forKey:@"internalframe"]; 325 [sRoles setObject:NSAccessibilityStaticTextRole forKey:@"label"]; 326 [sRoles setObject:JavaAccessibilityIgnore forKey:@"layeredpane"]; 327 [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." 328 [sRoles setObject:NSAccessibilityListRole forKey:@"listitem"]; 329 [sRoles setObject:NSAccessibilityMenuRole forKey:@"menu"]; 330 [sRoles setObject:NSAccessibilityMenuBarRole forKey:@"menubar"]; 331 [sRoles setObject:NSAccessibilityMenuItemRole forKey:@"menuitem"]; 332 [sRoles setObject:JavaAccessibilityIgnore forKey:@"optionpane"]; 333 [sRoles setObject:NSAccessibilityRadioButtonRole forKey:@"pagetab"]; // cmcnote: cocoa tabs are radio buttons - one selected button out of a group of options 334 [sRoles setObject:NSAccessibilityTabGroupRole forKey:@"pagetablist"]; 335 [sRoles setObject:JavaAccessibilityIgnore forKey:@"panel"]; 336 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"passwordtext"]; 337 [sRoles setObject:NSAccessibilityPopUpButtonRole forKey:@"popupmenu"]; 338 [sRoles setObject:NSAccessibilityProgressIndicatorRole forKey:@"progressbar"]; 339 [sRoles setObject:NSAccessibilityButtonRole forKey:@"pushbutton"]; 340 [sRoles setObject:NSAccessibilityRadioButtonRole forKey:@"radiobutton"]; 341 [sRoles setObject:JavaAccessibilityIgnore forKey:@"rootpane"]; 342 [sRoles setObject:NSAccessibilityRowRole forKey:@"rowheader"]; 343 [sRoles setObject:NSAccessibilityScrollBarRole forKey:@"scrollbar"]; 344 [sRoles setObject:NSAccessibilityScrollAreaRole forKey:@"scrollpane"]; 345 [sRoles setObject:JavaAccessibilityIgnore forKey:@"separator"]; 346 [sRoles setObject:NSAccessibilitySliderRole forKey:@"slider"]; 347 [sRoles setObject:NSAccessibilityIncrementorRole forKey:@"spinbox"]; 348 [sRoles setObject:NSAccessibilitySplitGroupRole forKey:@"splitpane"]; 349 [sRoles setObject:NSAccessibilityValueIndicatorRole forKey:@"statusbar"]; 350 [sRoles setObject:NSAccessibilityGroupRole forKey:@"swingcomponent"]; 351 [sRoles setObject:NSAccessibilityTableRole forKey:@"table"]; 352 [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"text"]; 353 [sRoles setObject:NSAccessibilityTextAreaRole forKey:@"textarea"]; // supports top/bottom of document notifications: CAccessability.getAccessibleRole() 354 [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"]; 355 [sRoles setObject:NSAccessibilityToolbarRole forKey:@"toolbar"]; 356 [sRoles setObject:JavaAccessibilityIgnore forKey:@"tooltip"]; 357 [sRoles setObject:NSAccessibilityBrowserRole forKey:@"tree"]; 358 [sRoles setObject:NSAccessibilityUnknownRole forKey:@"unknown"]; 359 [sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"]; 360 [sRoles setObject:JavaAccessibilityIgnore forKey:@"window"]; 361 }