From c05c8148a92cb7a86c17db6f7230aef1bc687c1e Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Tue, 8 Jun 2021 13:34:21 +0300 Subject: [PATCH 01/15] 8267385: Create NSAccessibilityElement implementation for JavaComponentAccessibility --- .../macosx/native/libawt_lwawt/awt/AWTView.m | 37 +- .../awt/JavaAccessibilityAction.h | 4 + .../awt/JavaAccessibilityAction.m | 32 + .../awt/JavaAccessibilityUtilities.h | 2 + .../awt/JavaAccessibilityUtilities.m | 54 + .../awt/JavaComponentAccessibility.m | 94 +- .../awt/a11y/ButtonAccessibility.m | 12 +- .../awt/a11y/CheckboxAccessibility.m | 2 +- .../awt/a11y/CommonComponentAccessibility.h | 61 +- .../awt/a11y/CommonComponentAccessibility.m | 924 +++++++++++++++++- .../awt/a11y/CommonTextAccessibility.m | 2 +- .../awt/a11y/GroupAccessibility.m | 12 +- .../awt/a11y/ImageAccessibility.m | 12 +- .../awt/a11y/RadiobuttonAccessibility.m | 2 +- .../awt/a11y/ScrollAreaAccessibility.m | 16 +- .../awt/a11y/SliderAccessibility.m | 14 +- .../awt/a11y/SpinboxAccessibility.m | 14 +- .../awt/a11y/StaticTextAccessibility.m | 10 + .../awt/a11y/AccessibleComponentTest.java | 115 +++ 19 files changed, 1291 insertions(+), 128 deletions(-) create mode 100644 test/jdk/java/awt/a11y/AccessibleComponentTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index 60da8dcac1d96..85d91ee4d8ca7 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -27,7 +27,7 @@ #import "CGLGraphicsConfig.h" #import "AWTView.h" #import "AWTWindow.h" -#import "JavaComponentAccessibility.h" +#import "a11y/CommonComponentAccessibility.h" #import "JavaTextAccessibility.h" #import "JavaAccessibilityUtilities.h" #import "GeomUtilities.h" @@ -611,42 +611,29 @@ + (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible - (id)getAxData:(JNIEnv*)env { jobject jcomponent = [self awtComponent:env]; - id ax = [[[JavaComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease]; + id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease]; (*env)->DeleteLocalRef(env, jcomponent); return ax; } -- (NSArray *)accessibilityAttributeNames -{ - return [[super accessibilityAttributeNames] arrayByAddingObject:NSAccessibilityChildrenAttribute]; -} - // NSAccessibility messages -// attribute methods -- (id)accessibilityAttributeValue:(NSString *)attribute +- (id)accessibilityChildren { AWT_ASSERT_APPKIT_THREAD; + JNIEnv *env = [ThreadUtilities getJNIEnv]; - if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) - { - JNIEnv *env = [ThreadUtilities getJNIEnv]; - - (*env)->PushLocalFrame(env, 4); + (*env)->PushLocalFrame(env, 4); - id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]); + id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]); - (*env)->PopLocalFrame(env, NULL); + (*env)->PopLocalFrame(env, NULL); - return result; - } - else - { - return [super accessibilityAttributeValue:attribute]; - } + return result; } -- (BOOL)accessibilityIsIgnored + +- (BOOL)isAccessibilityElement { - return YES; + return NO; } - (id)accessibilityHitTest:(NSPoint)point @@ -656,7 +643,7 @@ - (id)accessibilityHitTest:(NSPoint)point (*env)->PushLocalFrame(env, 4); - id result = [[self getAxData:env] accessibilityHitTest:point withEnv:env]; + id result = [[self getAxData:env] accessibilityHitTest:point]; (*env)->PopLocalFrame(env, NULL); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h index a628c4de5b16f..b8042db69b771 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.h @@ -26,6 +26,10 @@ #import #import +extern NSMutableDictionary *sActions; +extern NSMutableDictionary *sActionSelectors; +extern NSMutableArray *sAllActionSelectors; +void initializeActions(); @protocol JavaAccessibilityAction diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m index 03a5630b608a2..06e998ec4098e 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityAction.m @@ -29,6 +29,10 @@ #import "ThreadUtilities.h" #import "JNIUtilities.h" +NSMutableDictionary *sActions = nil; +NSMutableDictionary *sActionSelectors = nil; +NSMutableArray *sAllActionSelectors = nil; +void initializeActions(); @implementation JavaAxAction @@ -148,3 +152,31 @@ - (void)perform } @end + +void initializeActions() { + int actionsCount = 5; + + sActions = [[NSMutableDictionary alloc] initWithCapacity:actionsCount]; + + [sActions setObject:NSAccessibilityPressAction forKey:@"click"]; + [sActions setObject:NSAccessibilityIncrementAction forKey:@"increment"]; + [sActions setObject:NSAccessibilityDecrementAction forKey:@"decrement"]; + [sActions setObject:NSAccessibilityShowMenuAction forKey:@"togglePopup"]; + [sActions setObject:NSAccessibilityPressAction forKey:@"toggleExpand"]; + + sActionSelectors = [[NSMutableDictionary alloc] initWithCapacity:actionsCount]; + + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPress)) forKey:NSAccessibilityPressAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu)) forKey:NSAccessibilityShowMenuAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformDecrement)) forKey:NSAccessibilityDecrementAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformIncrement)) forKey:NSAccessibilityIncrementAction]; + [sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPick)) forKey:NSAccessibilityPickAction]; + + sAllActionSelectors = [[NSMutableArray alloc] initWithCapacity:actionsCount]; + + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPick))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformIncrement))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformDecrement))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu))]; + [sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPress))]; +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h index f3dfe247ede90..9c65f0d2e893d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h @@ -60,3 +60,5 @@ void JavaAccessibilitySetAttributeValue(id element, NSString *attribute, id valu void JavaAccessibilityRaiseSetAttributeToIllegalTypeException(const char *functionName, id element, NSString *attribute, id value); void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionName, id element, NSString *attribute); void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter); +BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); +NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index 12817e63ecd31..5061a35c4e9a7 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -27,6 +27,7 @@ #import "JNIUtilities.h" #import +#import "ThreadUtilities.h" static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute); static void JavaAccessibilityLogError(NSString *message); @@ -348,6 +349,59 @@ static void JavaAccessibilityLogError(NSString *message) NSLog(@"!!! %@", message); } +/* + * Returns Object.equals for the two items + * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock + * and try to pass a component so the event happens on the correct thread. + */ +BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) +{ + DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO); + DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO); + + if ((a == NULL) && (b == NULL)) return YES; + if ((a == NULL) || (b == NULL)) return NO; + + if (pthread_main_np() != 0) { + // If we are on the AppKit thread + DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO); + DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals", + "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO); + return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component); + CHECK_EXCEPTION(); + } + + jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b); + CHECK_EXCEPTION(); + return jb; +} + +/* + * The java/lang/Number concrete class could be for any of the Java primitive + * numerical types or some other subclass. + * All existing A11Y code uses Integer so that is what we look for first + * But all must be able to return a double and NSNumber accepts a double, + * so that's the fall back. + */ +NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) { + if (jnumber == NULL) { + return nil; + } + DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil); + DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil); + DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil); + DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil); + if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) { + jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue); + CHECK_EXCEPTION(); + return [NSNumber numberWithInteger:i]; + } else { + jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue); + CHECK_EXCEPTION(); + return [NSNumber numberWithDouble:d]; + } +} + // end appKit copies /* diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m index 33e838b070a1f..a2181ec1c31a0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m @@ -29,7 +29,6 @@ // #import "JavaComponentAccessibility.h" -#import "a11y/CommonComponentAccessibility.h" #import "sun_lwawt_macosx_CAccessibility.h" #import @@ -45,6 +44,12 @@ #import "JNIUtilities.h" #import "AWTView.h" +// these constants are duplicated in CAccessibility.java +#define JAVA_AX_ALL_CHILDREN (-1) +#define JAVA_AX_SELECTED_CHILDREN (-2) +#define JAVA_AX_VISIBLE_CHILDREN (-3) +// If the value is >=0, it's an index + // GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; @@ -82,7 +87,7 @@ static NSMutableDictionary *sAttributeNamesForRoleCache = nil; static NSObject *sAttributeNamesLOCK = nil; -@interface TabGroupAccessibility : JavaComponentAccessibility { +@interface TabGroupLegacyAccessibility : JavaComponentAccessibility { NSInteger _numTabs; } @@ -362,21 +367,18 @@ + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *) // otherwise, create a new instance JavaComponentAccessibility *newChild = nil; - newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole]; - if (newChild == nil) { - if ([javaRole isEqualToString:@"pagetablist"]) { - newChild = [TabGroupAccessibility alloc]; - } else if ([javaRole isEqualToString:@"table"]) { - newChild = [TableAccessibility alloc]; + if ([javaRole isEqualToString:@"pagetablist"]) { + newChild = [TabGroupLegacyAccessibility alloc]; + } else if ([javaRole isEqualToString:@"table"]) { + newChild = [TableAccessibility alloc]; + } else { + NSString *nsRole = [sRoles objectForKey:javaRole]; + if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || + [nsRole isEqualToString:NSAccessibilityTextAreaRole] || + [nsRole isEqualToString:NSAccessibilityTextFieldRole]) { + newChild = [JavaTextAccessibility alloc]; } else { - NSString *nsRole = [sRoles objectForKey:javaRole]; - if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || - [nsRole isEqualToString:NSAccessibilityTextAreaRole] || - [nsRole isEqualToString:NSAccessibilityTextFieldRole]) { - newChild = [JavaTextAccessibility alloc]; - } else { - newChild = [JavaComponentAccessibility alloc]; - } + newChild = [JavaComponentAccessibility alloc]; } } @@ -387,7 +389,7 @@ + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *) // This is the only way to know if the menu is opening; visible state change // can't be caught because the listeners are not set up in time. if ( [javaRole isEqualToString:@"popupmenu"] && - ![[parent javaRole] isEqualToString:@"combobox"] ) { + ![[parent javaRole] isEqualToString:@"combobox"] ) { [newChild postMenuOpened]; } @@ -904,32 +906,6 @@ - (NSInteger)accessibilityIndex { return index; } -/* - * The java/lang/Number concrete class could be for any of the Java primitive - * numerical types or some other subclass. - * All existing A11Y code uses Integer so that is what we look for first - * But all must be able to return a double and NSNumber accepts a double, - * so that's the fall back. - */ -static NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) { - if (jnumber == NULL) { - return nil; - } - DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil); - DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil); - DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil); - DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil); - if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) { - jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue); - CHECK_EXCEPTION(); - return [NSNumber numberWithInteger:i]; - } else { - jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue); - CHECK_EXCEPTION(); - return [NSNumber numberWithDouble:d]; - } -} - // Element's maximum value (id) - (id)accessibilityMaxValueAttribute { @@ -1600,7 +1576,7 @@ - (id)accessibilityFocusedUIElement JNI_COCOA_EXIT(env); } -@implementation TabGroupAccessibility +@implementation TabGroupLegacyAccessibility - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole { @@ -1802,9 +1778,6 @@ - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUI @end - -static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); - @implementation TabGroupControlAccessibility - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole @@ -1907,30 +1880,3 @@ - (id)accessibilityColumnCountAttribute { return [self getTableInfo:JAVA_AX_COLS]; } @end - -/* - * Returns Object.equals for the two items - * This may use LWCToolkit.invokeAndWait(); don't call while holding fLock - * and try to pass a component so the event happens on the correct thread. - */ -static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) -{ - DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO); - DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO); - - if ((a == NULL) && (b == NULL)) return YES; - if ((a == NULL) || (b == NULL)) return NO; - - if (pthread_main_np() != 0) { - // If we are on the AppKit thread - DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO); - DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals", - "(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO); - return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component); - CHECK_EXCEPTION(); - } - - jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b); - CHECK_EXCEPTION(); - return jb; -} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m index 912cef1fa67ac..8748d42bfa7fb 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ButtonAccessibility.m @@ -36,7 +36,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (BOOL)accessibilityPerformPress @@ -44,4 +44,14 @@ - (BOOL)accessibilityPerformPress return [self performAccessibleAction:0]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m index bbfaf39fe66bb..f2dbf60d92d4b 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m @@ -39,7 +39,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index 4140e0ea5f75e..82981bd219d9a 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -26,7 +26,9 @@ #ifndef JAVA_COMPONENT_ACCESSIBILITY #define JAVA_COMPONENT_ACCESSIBILITY -#import "JavaComponentAccessibility.h" +#include "jni.h" + +#import #import "JavaAccessibilityUtilities.h" // these constants are duplicated in CAccessibility.java @@ -35,14 +37,65 @@ #define JAVA_AX_VISIBLE_CHILDREN (-3) // If the value is >=0, it's an index -@interface CommonComponentAccessibility : JavaComponentAccessibility { +@interface CommonComponentAccessibility : NSAccessibilityElement { + NSView *fView; + NSObject *fParent; + + NSString *fNSRole; + NSString *fJavaRole; + + jint fIndex; + jobject fAccessible; + jobject fComponent; + NSMutableDictionary *fActions; + NSMutableArray *fActionSelectors; + NSObject *fActionsLOCK; } + +@property(nonnull, readonly) NSArray *actionSelectors; + +- (id _Nonnull)initWithParent:(NSObject* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withAccessible:(jobject _Nullable)accessible withIndex:(jint)index withView:(NSView* _Nonnull)view withJavaRole:(NSString* _Nullable)javaRole; +- (void)unregisterFromCocoaAXSystem; +- (void)postValueChanged; +- (void)postSelectedTextChanged; +- (void)postSelectionChanged; +- (void)postTitleChanged; +- (BOOL)isEqual:(nonnull id)anObject; +- (BOOL)isAccessibleWithEnv:(JNIEnv _Nonnull * _Nonnull)env forAccessible:(nonnull jobject)accessible; + ++ (void)postFocusChanged:(nullable id)message; + + (void) initializeRolesMap; -+ (JavaComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role; + ++ (CommonComponentAccessibility* _Nullable) getComponentAccessibility:(NSString* _Nonnull)role; + ++ (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored; ++ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)role index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + +- (jobject _Nullable)axContextWithEnv:(JNIEnv _Nonnull * _Nonnull)env; +- (NSView* _Nonnull)view; +- (NSWindow* _Nonnull)window; +- (id _Nonnull)parent; +- (NSString* _Nonnull)javaRole; + +- (BOOL)isMenu; +- (BOOL)isSelected:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)isSelectable:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)isVisible:(JNIEnv _Nonnull * _Nonnull)env; + +- (NSArray* _Nullable)accessibleChildrenWithChildCode:(NSInteger)childCode; + +- (NSDictionary* _Nullable)getActions:(JNIEnv _Nonnull * _Nonnull)env; +- (void)getActionsWithEnv:(JNIEnv _Nonnull * _Nonnull)env; +- (BOOL)accessiblePerformAction:(NSAccessibilityActionName _Nonnull)actionName; + +- (BOOL)performAccessibleAction:(int)index; + - (NSRect)accessibilityFrame; - (id _Nullable)accessibilityParent; -- (BOOL)performAccessibleAction:(int)index; - (BOOL)isAccessibilityElement; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 25317afa3b3ce..ee67dc7dc40ed 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -24,17 +24,46 @@ */ #import "CommonComponentAccessibility.h" -#import "JNIUtilities.h" +#import +#import +#import +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" #import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "AWTView.h" +// GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; -static jmethodID sjm_getAccessibleComponent = NULL; +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +static jmethodID sjm_getAccessibleComponent = NULL; #define GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(ret) \ GET_CACCESSIBILITY_CLASS_RETURN(ret); \ GET_STATIC_METHOD_RETURN(sjm_getAccessibleComponent, sjc_CAccessibility, "getAccessibleComponent", \ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleComponent;", ret); +static jmethodID sjm_getAccessibleIndexInParent = NULL; +#define GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleIndexInParent, sjc_CAccessibility, "getAccessibleIndexInParent", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I", ret); + +static jclass sjc_CAccessible = NULL; +#define GET_CACCESSIBLE_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret); + static NSMutableDictionary * _Nullable rolesMap; NSString *const IgnoreClassName = @"IgnoreAccessibility"; static jobject sAccessibilityClass = NULL; @@ -44,6 +73,41 @@ */ @implementation CommonComponentAccessibility +- (BOOL)isMenu +{ + id role = [self accessibilityRole]; + return [role isEqualToString:NSAccessibilityMenuBarRole] || [role isEqualToString:NSAccessibilityMenuRole] || [role isEqualToString:NSAccessibilityMenuItemRole]; +} + +- (BOOL)isSelected:(JNIEnv *)env +{ + if (fIndex == -1) { + return NO; + } + + return isChildSelected(env, ((CommonComponentAccessibility *)[self parent])->fAccessible, fIndex, fComponent); +} + +- (BOOL)isSelectable:(JNIEnv *)env +{ + jobject axContext = [self axContextWithEnv:env]; + BOOL selectable = isSelectable(env, axContext, fComponent); + (*env)->DeleteLocalRef(env, axContext); + return selectable; +} + +- (BOOL)isVisible:(JNIEnv *)env +{ + if (fIndex == -1) { + return NO; + } + + jobject axContext = [self axContextWithEnv:env]; + BOOL showing = isShowing(env, axContext, fComponent); + (*env)->DeleteLocalRef(env, axContext); + return showing; +} + + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes @@ -132,7 +196,7 @@ + (void) initializeRolesMap { * If new implementation of the accessible component peer for the given role exists * return the allocated class otherwise return nil to let old implementation being initialized */ -+ (JavaComponentAccessibility *) getComponentAccessibility:(NSString *)role ++ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role { AWT_ASSERT_APPKIT_THREAD; if (rolesMap == nil) { @@ -143,10 +207,468 @@ + (JavaComponentAccessibility *) getComponentAccessibility:(NSString *)role if (className != nil) { return [NSClassFromString(className) alloc]; } - return nil; + return [CommonComponentAccessibility alloc]; +} + +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole +{ + self = [super init]; + if (self) + { + fParent = [parent retain]; + fView = [view retain]; + fJavaRole = [javaRole retain]; + + fAccessible = (*env)->NewWeakGlobalRef(env, accessible); + (*env)->ExceptionClear(env); // in case of OOME + jobject jcomponent = [(AWTView *)fView awtComponent:env]; + fComponent = (*env)->NewWeakGlobalRef(env, jcomponent); + (*env)->DeleteLocalRef(env, jcomponent); + + fIndex = index; + + fActions = nil; + fActionSelectors = nil; + fActionsLOCK = [[NSObject alloc] init]; + } + return self; +} + +- (void)unregisterFromCocoaAXSystem +{ + AWT_ASSERT_APPKIT_THREAD; + static dispatch_once_t initialize_unregisterUniqueId_once; + static void (*unregisterUniqueId)(id); + dispatch_once(&initialize_unregisterUniqueId_once, ^{ + void *jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); + unregisterUniqueId = dlsym(jrsFwk, "JRSAccessibilityUnregisterUniqueIdForUIElement"); + }); + if (unregisterUniqueId) unregisterUniqueId(self); +} + +- (void)dealloc +{ + [self unregisterFromCocoaAXSystem]; + + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; + + (*env)->DeleteWeakGlobalRef(env, fAccessible); + fAccessible = NULL; + + (*env)->DeleteWeakGlobalRef(env, fComponent); + fComponent = NULL; + + [fParent release]; + fParent = nil; + + [fNSRole release]; + fNSRole = nil; + + [fJavaRole release]; + fJavaRole = nil; + + [fView release]; + fView = nil; + + [fActions release]; + fActions = nil; + + [fActionsLOCK release]; + fActionsLOCK = nil; + + [fActionSelectors release]; + fActionSelectors = nil; + + [super dealloc]; +} + +- (void)postValueChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification); +} + +- (void)postSelectedTextChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedTextChangedNotification); +} + +- (void)postSelectionChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification); +} + +-(void)postTitleChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilityTitleChangedNotification); +} + +- (void)postMenuOpened +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuOpenedNotification); +} + +- (void)postMenuClosed +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuClosedNotification); +} + +- (void)postMenuItemSelected +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, (NSString *)kAXMenuItemSelectedNotification); +} + +- (BOOL)isEqual:(id)anObject +{ + if (![anObject isKindOfClass:[self class]]) return NO; + CommonComponentAccessibility *accessibility = (CommonComponentAccessibility *)anObject; + + JNIEnv* env = [ThreadUtilities getJNIEnv]; + return (*env)->IsSameObject(env, accessibility->fAccessible, fAccessible); +} + +- (BOOL)isAccessibleWithEnv:(JNIEnv *)env forAccessible:(jobject)accessible +{ + return (*env)->IsSameObject(env, fAccessible, accessible); +} + ++ (void)initialize +{ + if (sRoles == nil) { + initializeRoles(); + } + if (sActions == nil) { + initializeActions(); + } +} + ++ (void)postFocusChanged:(id)message +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification); +} + ++ (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env { + DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", NULL); + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", + "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); + if ((*env)->IsInstanceOf(env, jaccessible, sjc_CAccessible)) { + return jaccessible; + } else if ((*env)->IsInstanceOf(env, jaccessible, sjc_Accessible)) { + jobject o = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, jaccessible); + CHECK_EXCEPTION(); + return o; + } + return NULL; +} + ++ (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored +{ + if (parent->fAccessible == NULL) return nil; + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); + CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child + + NSInteger i; + NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly + for(i = 0; i < arrayLen; i+=2) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CommonComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView]; + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + [children addObject:child]; + childIndex++; + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + + return children; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view +{ + GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(nil); + CommonComponentAccessibility *ret = nil; + jobject jcomponent = [(AWTView *)view awtComponent:env]; + jint index = (*env)->CallStaticIntMethod(env, sjc_CAccessibility, sjm_getAccessibleIndexInParent, jaccessible, jcomponent); + CHECK_EXCEPTION(); + if (index >= 0) { + NSString *javaRole = getJavaRole(env, jaccessible, jcomponent); + ret = [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view]; + } + (*env)->DeleteLocalRef(env, jcomponent); + return ret; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; +} + ++ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view +{ + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_FIELD_RETURN(jf_ptr, sjc_CAccessible, "ptr", "J", NULL); + // try to fetch the jCAX from Java, and return autoreleased + jobject jCAX = [CommonComponentAccessibility getCAccessible:jaccessible withEnv:env]; + if (jCAX == NULL) return nil; + CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); + if (value != nil) { + (*env)->DeleteLocalRef(env, jCAX); + return [[value retain] autorelease]; + } + + // otherwise, create a new instance + CommonComponentAccessibility *newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole];; + + // must init freshly -alloc'd object + [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance + + // If creating a JPopupMenu (not a combobox popup list) need to fire menuOpened. + // This is the only way to know if the menu is opening; visible state change + // can't be caught because the listeners are not set up in time. + if ( [javaRole isEqualToString:@"popupmenu"] && + ![[parent javaRole] isEqualToString:@"combobox"] ) { + [newChild postMenuOpened]; + } + + // must hard retain pointer poked into Java object + [newChild retain]; + (*env)->SetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); + (*env)->DeleteLocalRef(env, jCAX); + + // return autoreleased instance + return [newChild autorelease]; +} + +- (NSDictionary *)getActions:(JNIEnv *)env +{ + @synchronized(fActionsLOCK) { + if (fActions == nil) { + [self getActionsWithEnv:env]; + } + } + + return fActions; +} + +- (void)getActionsWithEnv:(JNIEnv *)env +{ + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_getAccessibleAction, sjc_CAccessibility, "getAccessibleAction", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleAction;"); + + jobject axAction = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getAccessibleAction, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axAction != NULL) { + jclass jc_AccessibleAction = NULL; + GET_CLASS(jc_AccessibleAction, "javax/accessibility/AccessibleAction"); + jmethodID jm_getAccessibleActionCount = NULL; + GET_METHOD(jm_getAccessibleActionCount, jc_AccessibleAction, "getAccessibleActionCount", "()I"); + jint count = (*env)->CallIntMethod(env, axAction, jm_getAccessibleActionCount); + fActions = [[NSMutableDictionary alloc] initWithCapacity:count]; + fActionSelectors = [[NSMutableArray alloc] initWithCapacity:count]; + for (int i =0; i < count; i++) { + JavaAxAction *action = [[JavaAxAction alloc] initWithEnv:env withAccessibleAction:axAction withIndex:i withComponent:fComponent]; + if ([fParent isKindOfClass:[CommonComponentAccessibility class]] && + [(CommonComponentAccessibility *)fParent isMenu] && + [[sActions objectForKey:[action getDescription]] isEqualToString:NSAccessibilityPressAction]) { + [fActions setObject:action forKey:NSAccessibilityPickAction]; + [fActionSelectors addObject:[sActionSelectors objectForKey:NSAccessibilityPickAction]]; + } else { + [fActions setObject:action forKey:[sActions objectForKey:[action getDescription]]]; + [fActionSelectors addObject:[sActionSelectors objectForKey:[sActions objectForKey:[action getDescription]]]]; + } + [action release]; + } + (*env)->DeleteLocalRef(env, axAction); + } +} + +- (BOOL)accessiblePerformAction:(NSAccessibilityActionName)actionName { + NSMutableDictionary *currentAction = [self getActions:[ThreadUtilities getJNIEnv]]; + if (currentAction == nil) { + return NO; + } + if ([[currentAction allKeys] containsObject:actionName]) { + [(JavaAxAction *)[currentAction objectForKey:actionName] perform]; + return YES;; + } + return NO; +} + +- (NSArray *)actionSelectors { + @synchronized(fActionsLOCK) { + if (fActionSelectors == nil) { + [self getActionsWithEnv:[ThreadUtilities getJNIEnv]]; + } + } + + return [NSArray arrayWithArray:fActionSelectors]; +} + +- (NSArray *)accessibleChildrenWithChildCode:(NSInteger)childCode +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:childCode + allowIgnored:NO]; + + NSArray *value = nil; + if ([children count] > 0) { + value = children; + } + + return value; +} + +- (NSView *)view +{ + return fView; +} + +- (NSWindow *)window +{ + return [[self view] window]; +} + +- (id)parent +{ + if(fParent == nil) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleParent, sjc_CAccessibility, "getAccessibleParent", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + GET_CACCESSIBLE_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getSwingAccessible, sjc_CAccessible, "getSwingAccessible", + "(Ljavax/accessibility/Accessible;)Ljavax/accessibility/Accessible;", nil); + DECLARE_CLASS_RETURN(sjc_Window, "java/awt/Window", nil); + + jobject jparent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleParent, fAccessible, fComponent); + CHECK_EXCEPTION(); + + if (jparent == NULL) { + fParent = fView; + } else { + AWTView *view = fView; + jobject jax = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getSwingAccessible, fAccessible); + CHECK_EXCEPTION(); + + if ((*env)->IsInstanceOf(env, jax, sjc_Window)) { + // In this case jparent is an owner toplevel and we should retrieve its own view + view = [AWTView awtView:env ofAccessible:jparent]; + } + if (view != nil) { + fParent = [CommonComponentAccessibility createWithAccessible:jparent withEnv:env withView:view]; + } + if (fParent == nil) { + fParent = fView; + } + (*env)->DeleteLocalRef(env, jparent); + (*env)->DeleteLocalRef(env, jax ); + } + [fParent retain]; + } + return fParent; +} + +- (NSString *)javaRole +{ + if(fJavaRole == nil) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + fJavaRole = getJavaRole(env, fAccessible, fComponent); + [fJavaRole retain]; + } + return fJavaRole; +} + +- (jobject)axContextWithEnv:(JNIEnv *)env +{ + return getAxContext(env, fAccessible, fComponent); } // NSAccessibilityElement protocol implementation + +- (BOOL)isAccessibilityElement +{ + return ![[self accessibilityRole] isEqualToString:JavaAccessibilityIgnore]; +} + +- (NSString *)accessibilityLabel +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject val = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (val == NULL) { + return nil; + } + NSString* str = JavaStringToNSString(env, val); + (*env)->DeleteLocalRef(env, val); + return str; +} + +- (NSString *)accessibilityHelp +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleDescription, sjc_CAccessibility, "getAccessibleDescription", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + jobject val = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleDescription, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (val == NULL) { + return nil; + } + NSString* str = JavaStringToNSString(env, val); + (*env)->DeleteLocalRef(env, val); + return str; +} + +- (NSArray *)accessibilityChildren +{ + return [self accessibleChildrenWithChildCode:JAVA_AX_ALL_CHILDREN]; +} + +- (NSArray *)accessibilitySelectedChildren +{ + return [self accessibleChildrenWithChildCode:JAVA_AX_SELECTED_CHILDREN]; +} + +- (NSArray *)accessibilityVisibleChildren +{ + return [self accessibleChildrenWithChildCode:JAVA_AX_VISIBLE_CHILDREN]; +} + - (NSRect)accessibilityFrame { JNIEnv* env = [ThreadUtilities getJNIEnv]; @@ -168,10 +690,372 @@ - (NSRect)accessibilityFrame - (id _Nullable)accessibilityParent { - return [self accessibilityParentAttribute]; + return NSAccessibilityUnignoredAncestor([self parent]); +} + +- (BOOL)isAccessibilityEnabled +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(NO); + DECLARE_STATIC_METHOD_RETURN(jm_isEnabled, sjc_CAccessibility, "isEnabled", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO); + + BOOL value = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, jm_isEnabled, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (!value) { + NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self); + } + return value; +} + +- (id)accessibilityApplicationFocusedUIElement +{ + return [self accessibilityFocusedUIElement]; +} + +- (NSAccessibilityRole)accessibilityRole +{ + if (fNSRole == nil) { + NSString *javaRole = [self javaRole]; + fNSRole = [sRoles objectForKey:javaRole]; + // The sRoles NSMutableDictionary maps popupmenu to Mac's popup button. + // JComboBox behavior currently relies on this. However this is not the + // proper mapping for a JPopupMenu so fix that. + if ( [javaRole isEqualToString:@"popupmenu"] && + ![[[self parent] javaRole] isEqualToString:@"combobox"] ) { + fNSRole = NSAccessibilityMenuRole; + } + if (fNSRole == nil) { + // this component has assigned itself a custom AccessibleRole not in the sRoles array + fNSRole = javaRole; + } + [fNSRole retain]; + } + return fNSRole; +} + +- (NSString *)accessibilityRoleDescription +{ + // first ask AppKit for its accessible role description for a given AXRole + NSString *value = NSAccessibilityRoleDescription([self accessibilityRole], nil); + + if (value == nil) { + // query java if necessary + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getAccessibleRoleDisplayString, sjc_CAccessibility, "getAccessibleRoleDisplayString", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + + jobject axRole = (*env)->CallStaticObjectMethod(env, jm_getAccessibleRoleDisplayString, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axRole != NULL) { + value = JavaStringToNSString(env, axRole); + (*env)->DeleteLocalRef(env, axRole); + } else { + value = @"unknown"; + } + } + + return value; +} + +- (BOOL)isAccessibilityFocused +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(NO); + DECLARE_STATIC_METHOD_RETURN(sjm_isFocusTraversable, sjc_CAccessibility, "isFocusTraversable", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO); + // According to javadoc, a component that is focusable will return true from isFocusTraversable, + // as well as having AccessibleState.FOCUSABLE in its AccessibleStateSet. + // We use the former heuristic; if the component focus-traversable, add a focused attribute + // See also initializeAttributeNamesWithEnv: + if ((*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isFocusTraversable, fAccessible, fComponent)) { + return [self isEqual:[NSApp accessibilityFocusedUIElement]]; + } + CHECK_EXCEPTION(); + + return NO; +} + +- (void)setAccessibilityFocused:(BOOL)accessibilityFocused +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_requestFocus, sjc_CAccessibility, "requestFocus", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); + + if (accessibilityFocused) + { + (*env)->CallStaticVoidMethod(env, sjc_CAccessibility, jm_requestFocus, fAccessible, fComponent); + CHECK_EXCEPTION(); + } +} + +- (NSUInteger)accessibilityIndexOfChild:(id)child +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(0); + jint returnValue = + (*env)->CallStaticIntMethod( env, + sjc_CAccessibility, + sjm_getAccessibleIndexInParent, + ((CommonComponentAccessibility *)child)->fAccessible, + ((CommonComponentAccessibility *)child)->fComponent ); + CHECK_EXCEPTION(); + return (returnValue == -1) ? NSNotFound : returnValue; +} + +- (NSInteger)accessibilityIndex +{ + int index = 0; + if (fParent != NULL) { + index = [fParent accessibilityIndexOfChild:self]; + } + return index; +} + +- (id)accessibilityMaxValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getMaximumAccessibleValue, sjc_CAccessibility, "getMaximumAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;", nil); + + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getMaximumAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue == NULL) { + return [NSNumber numberWithInt:0]; + } + NSNumber* num = JavaNumberToNSNumber(env, axValue); + (*env)->DeleteLocalRef(env, axValue); + return num; +} + +- (id)accessibilityMinValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getMinimumAccessibleValue, sjc_CAccessibility, "getMinimumAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/Number;", nil); + + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getMinimumAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue == NULL) { + return [NSNumber numberWithInt:0]; + } + NSNumber* num = JavaNumberToNSNumber(env, axValue); + (*env)->DeleteLocalRef(env, axValue); + return num; +} + +- (NSAccessibilityOrientation)accessibilityOrientation +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + + // cmcnote - should batch these two calls into one that returns an array of two bools, one for vertical and one for horiz + if (isVertical(env, axContext, fComponent)) { + (*env)->DeleteLocalRef(env, axContext); + return NSAccessibilityOrientationVertical; + } + if (isHorizontal(env, axContext, fComponent)) { + (*env)->DeleteLocalRef(env, axContext); + return NSAccessibilityOrientationHorizontal; + } + return NSAccessibilityOrientationUnknown; +} + +- (NSPoint)accessibilityActivationPoint +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(NSPointFromString(@"")); + jobject axComponent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleComponent, + fAccessible, fComponent); + CHECK_EXCEPTION(); + + // NSAccessibility wants the bottom left point of the object in + // bottom left based screen coords + + // Get the java screen coords, and make a NSPoint of the bottom left of the AxComponent. + NSSize size = getAxComponentSize(env, axComponent, fComponent); + NSPoint point = getAxComponentLocationOnScreen(env, axComponent, fComponent); + (*env)->DeleteLocalRef(env, axComponent); + + point.y += size.height; + + // Now make it into Cocoa screen coords. + point.y = [[[[self view] window] screen] frame].size.height - point.y; + + return point; +} + +- (BOOL)isAccessibilitySelected +{ + return [self isSelected:[ThreadUtilities getJNIEnv]]; +} + +- (void)setAccessibilitySelected:(BOOL)accessibilitySelected +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS(); + DECLARE_STATIC_METHOD(jm_requestSelection, + sjc_CAccessibility, + "requestSelection", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V"); + + if (accessibilitySelected) { + (*env)->CallStaticVoidMethod(env, sjc_CAccessibility, jm_requestSelection, fAccessible, fComponent); + CHECK_EXCEPTION(); + } +} + +- (id)accessibilityValue +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + // Need to handle popupmenus differently. + // + // At least for now don't handle combo box menus. + // This may change when later fixing issues which currently + // exist for combo boxes, but for now the following is only + // for JPopupMenus, not for combobox menus. + id parent = [self parent]; + if ( [[self javaRole] isEqualToString:@"popupmenu"] && + ![[parent javaRole] isEqualToString:@"combobox"] ) { + NSArray *children = + [CommonComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:JAVA_AX_ALL_CHILDREN + allowIgnored:YES]; + if ([children count] > 0) { + // handle case of AXMenuItem + // need to ask menu what is selected + NSArray *selectedChildrenOfMenu = + [self accessibilitySelectedChildren]; + CommonComponentAccessibility *selectedMenuItem = + [selectedChildrenOfMenu objectAtIndex:0]; + if (selectedMenuItem != nil) { + GET_CACCESSIBILITY_CLASS_RETURN(nil); + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject itemValue = + (*env)->CallStaticObjectMethod( env, + sjm_getAccessibleName, + selectedMenuItem->fAccessible, + selectedMenuItem->fComponent ); + CHECK_EXCEPTION(); + if (itemValue == NULL) { + return nil; + } + NSString* itemString = JavaStringToNSString(env, itemValue); + (*env)->DeleteLocalRef(env, itemValue); + return itemString; + } else { + return nil; + } + } + } + + // ask Java for the component's accessibleValue. In java, the "accessibleValue" just means a numerical value + // a text value is taken care of in JavaTextAccessibility + + // cmcnote should coalesce these calls into one java call + NSNumber *num = nil; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleValue, sjc_CAccessibility, "getAccessibleValue", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleValue;", nil); + jobject axValue = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleValue, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axValue != NULL) { + DECLARE_STATIC_METHOD_RETURN(jm_getCurrentAccessibleValue, sjc_CAccessibility, "getCurrentAccessibleValue", + "(Ljavax/accessibility/AccessibleValue;Ljava/awt/Component;)Ljava/lang/Number;", nil); + jobject str = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getCurrentAccessibleValue, axValue, fComponent); + CHECK_EXCEPTION(); + if (str != NULL) { + num = JavaNumberToNSNumber(env, str); + (*env)->DeleteLocalRef(env, str); + } + (*env)->DeleteLocalRef(env, axValue); + } + if (num == nil) { + num = [NSNumber numberWithInt:0]; + } + return num; +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + + DECLARE_CLASS_RETURN(jc_Container, "java/awt/Container", nil); + DECLARE_STATIC_METHOD_RETURN(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", + "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;", nil); + + // Make it into java screen coords + point.y = [[[[self view] window] screen] frame].size.height - point.y; + + jobject jparent = fComponent; + + id value = nil; + if ((*env)->IsInstanceOf(env, jparent, jc_Container)) { + jobject jaccessible = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_accessibilityHitTest, + jparent, (jfloat)point.x, (jfloat)point.y); + CHECK_EXCEPTION(); + if (jaccessible != NULL) { + value = [CommonComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:fView]; + (*env)->DeleteLocalRef(env, jaccessible); + } + } + + if (value == nil) { + value = self; + } + + if (![value isAccessibilityElement]) { + value = NSAccessibilityUnignoredAncestor(value); + } + +#ifdef JAVA_AX_DEBUG + NSLog(@"%s: %@", __FUNCTION__, value); +#endif + return value; +} + +- (id)accessibilityFocusedUIElement +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getFocusOwner, sjc_CAccessibility, "getFocusOwner", + "(Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + id value = nil; + + NSWindow* hostWindow = [[self->fView window] retain]; + jobject focused = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getFocusOwner, fComponent); + [hostWindow release]; + CHECK_EXCEPTION(); + + if (focused != NULL) { + DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", nil); + if ((*env)->IsInstanceOf(env, focused, sjc_Accessible)) { + value = [CommonComponentAccessibility createWithAccessible:focused withEnv:env withView:fView]; + } + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, focused); + } + + if (value == nil) { + value = self; + } +#ifdef JAVA_AX_DEBUG + NSLog(@"%s: %@", __FUNCTION__, value); +#endif + return value; +} + +- (id)accessibilityWindow { + return [self window]; } // AccessibleAction support + - (BOOL)performAccessibleAction:(int)index { AWT_ASSERT_APPKIT_THREAD; @@ -187,8 +1071,34 @@ - (BOOL)performAccessibleAction:(int)index return TRUE; } -- (BOOL)isAccessibilityElement { - return YES; +// NSAccessibilityActions methods + +- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector { + if ([sAllActionSelectors containsObject:NSStringFromSelector(selector)] && + ![[self actionSelectors] containsObject:NSStringFromSelector(selector)]) { + return NO; + } + return [super isAccessibilitySelectorAllowed:selector]; +} + +- (BOOL)accessibilityPerformPick { + return [self accessiblePerformAction:NSAccessibilityPickAction]; +} + +- (BOOL)accessibilityPerformPress { + return [self accessiblePerformAction:NSAccessibilityPressAction]; +} + +- (BOOL)accessibilityPerformShowMenu { + return [self accessiblePerformAction:NSAccessibilityShowMenuAction]; +} + +- (BOOL)accessibilityPerformDecrement { + return [self accessiblePerformAction:NSAccessibilityDecrementAction]; +} + +- (BOOL)accessibilityPerformIncrement { + return [self accessiblePerformAction:NSAccessibilityIncrementAction]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m index 007cbe9de8c54..18e3f47e1ebba 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonTextAccessibility.m @@ -70,7 +70,7 @@ - (nullable NSString *)accessibilityValueAttribute GET_CACCESSIBILITY_CLASS_RETURN(nil); DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); - if ([[self accessibilityRoleAttribute] isEqualToString:NSAccessibilityStaticTextRole]) { + if ([[self accessibilityRole] isEqualToString:NSAccessibilityStaticTextRole]) { jobject axName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, fAccessible, fComponent); CHECK_EXCEPTION(); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m index 1a33b6a003a77..ff5450a4beff8 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m @@ -43,7 +43,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSArray *)accessibilityChildren { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; @@ -55,4 +55,14 @@ - (NSArray *)accessibilityChildren { } } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m index 4882d709a0169..63701a180c90f 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ImageAccessibility.m @@ -36,7 +36,17 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m index 8254b63df3d5a..7c2ce4a473561 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/RadiobuttonAccessibility.m @@ -39,7 +39,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m index 71c8de3de6ac7..537dc039670c2 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m @@ -35,16 +35,16 @@ @implementation ScrollAreaAccessibility - (NSArray * _Nullable)accessibilityContentsAttribute { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]]; // The scroll bars are in the children. children less the scroll bars is the contents NSEnumerator *enumerator = [children objectEnumerator]; - JavaComponentAccessibility *aElement; - while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { - if (![[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { + CommonComponentAccessibility *aElement; + while ((aElement = (CommonComponentAccessibility *)[enumerator nextObject])) { + if (![[aElement accessibilityRole] isEqualToString:NSAccessibilityScrollBarRole]) { // no scroll bars in contents [(NSMutableArray *)contents addObject:aElement]; } @@ -56,14 +56,14 @@ - (id _Nullable)getScrollBarwithOrientation:(enum NSAccessibilityOrientation)ori { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [JavaComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; // The scroll bars are in the children. - JavaComponentAccessibility *aElement; + CommonComponentAccessibility *aElement; NSEnumerator *enumerator = [children objectEnumerator]; - while ((aElement = (JavaComponentAccessibility *)[enumerator nextObject])) { - if ([[aElement accessibilityRoleAttribute] isEqualToString:NSAccessibilityScrollBarRole]) { + while ((aElement = (CommonComponentAccessibility *)[enumerator nextObject])) { + if ([[aElement accessibilityRole] isEqualToString:NSAccessibilityScrollBarRole]) { jobject elementAxContext = [aElement axContextWithEnv:env]; if (orientation == NSAccessibilityOrientationHorizontal) { if (isHorizontal(env, elementAxContext, fComponent)) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m index 5324b7e1afd4f..39cb21f3149b1 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SliderAccessibility.m @@ -39,12 +39,12 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (id _Nullable)accessibilityValue { - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } - (BOOL)accessibilityPerformIncrement @@ -57,4 +57,14 @@ - (BOOL)accessibilityPerformDecrement return [self performAccessibleAction:DECREMENT]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m index 7290a2ee8484d..4dac6bd93f953 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m @@ -39,12 +39,12 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole - (NSString * _Nullable)accessibilityLabel { - return [self accessibilityTitleAttribute]; + return [super accessibilityLabel]; } - (id _Nullable)accessibilityValue { - return [self accessibilityValueAttribute]; + return [super accessibilityValue]; } - (BOOL)accessibilityPerformIncrement @@ -58,4 +58,14 @@ - (BOOL)accessibilityPerformDecrement return [self performAccessibleAction:DECREMENT]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m index 8e905c45c6438..b54a32be5eba9 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m @@ -47,4 +47,14 @@ - (NSRange)accessibilityVisibleCharacterRange return [self accessibilityVisibleCharacterRangeAttribute]; } +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + @end diff --git a/test/jdk/java/awt/a11y/AccessibleComponentTest.java b/test/jdk/java/awt/a11y/AccessibleComponentTest.java new file mode 100644 index 0000000000000..de258727f4ca3 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleComponentTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.*; +import java.security.PublicKey; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.CountDownLatch; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.*; +import javax.swing.JPanel; + +public abstract class AccessibleComponentTest { + + protected static volatile boolean testResult = true; + protected static volatile CountDownLatch countDownLatch; + protected static String INSTRUCTIONS; + protected static String exceptionString; + protected JFrame mainFrame; + protected static AccessibleComponentTest a11yTest; + + public abstract CountDownLatch createCountDownLatch(); + + public void createUI(JPanel component, String testName) { + mainFrame = new JFrame(testName); + GridBagLayout layout = new GridBagLayout(); + JPanel mainControlPanel = new JPanel(layout); + JPanel resultButtonPanel = new JPanel(layout); + + GridBagConstraints gbc = new GridBagConstraints(); + + JTextArea instructionTextArea = new JTextArea(); + instructionTextArea.setText(INSTRUCTIONS); + instructionTextArea.setEditable(false); + instructionTextArea.setBackground(Color.white); + + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainControlPanel.add(instructionTextArea, gbc); + gbc.gridx = 0; + gbc.gridy = 1; + mainControlPanel.add(component); + + JButton passButton = new JButton("Pass"); + passButton.setActionCommand("Pass"); + passButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + + JButton failButton = new JButton("Fail"); + failButton.setActionCommand("Fail"); + failButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + testResult = false; + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + + gbc.gridx = 0; + gbc.gridy = 0; + resultButtonPanel.add(passButton, gbc); + + gbc.gridx = 1; + gbc.gridy = 0; + resultButtonPanel.add(failButton, gbc); + + gbc.gridx = 0; + gbc.gridy = 2; + mainControlPanel.add(resultButtonPanel, gbc); + + mainFrame.add(mainControlPanel); + mainFrame.pack(); + + mainFrame.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + mainFrame.dispose(); + countDownLatch.countDown(); + } + }); + mainFrame.setVisible(true); + } +} From 66ce693b7f5472683908129f79bb58f948348eb9 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 14:47:24 +0300 Subject: [PATCH 02/15] 8262031: Create implementation for NSAccessibilityNavigableStaticText protocol --- .../macosx/native/libawt_lwawt/awt/AWTView.m | 24 +- .../awt/JavaAccessibilityUtilities.h | 1 + .../awt/JavaAccessibilityUtilities.m | 16 + .../libawt_lwawt/awt/JavaTextAccessibility.m | 17 - .../awt/a11y/CommonComponentAccessibility.m | 6 +- .../awt/a11y/NavigableTextAccessibility.h | 31 ++ .../awt/a11y/NavigableTextAccessibility.m | 320 ++++++++++++++++++ .../jdk/java/awt/a11y/AccessibleTextTest.java | 285 ++++++++++++++++ 8 files changed, 673 insertions(+), 27 deletions(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleTextTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index 85d91ee4d8ca7..130a2611f0a14 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -28,7 +28,6 @@ #import "AWTView.h" #import "AWTWindow.h" #import "a11y/CommonComponentAccessibility.h" -#import "JavaTextAccessibility.h" #import "JavaAccessibilityUtilities.h" #import "GeomUtilities.h" #import "ThreadUtilities.h" @@ -668,17 +667,24 @@ - (id)accessibilityFocusedUIElement // --- Services menu support for lightweights --- // finds the focused accessible element, and if it is a text element, obtains the text from it -- (NSString *)accessibleSelectedText +- (NSString *)accessibilitySelectedText { id focused = [self accessibilityFocusedUIElement]; - if (![focused isKindOfClass:[JavaTextAccessibility class]]) return nil; - return [(JavaTextAccessibility *)focused accessibilitySelectedTextAttribute]; + if (![focused respondsToSelector:@selector(accessibilitySelectedText)]) return nil; + return [focused accessibilitySelectedText]; +} + +- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText { + id focused = [self accessibilityFocusedUIElement]; + if ([focused respondsToSelector:@selector(setAccessibilitySelectedText)]) { + [focused setAccessibilitySelectedText:accessibilitySelectedText]; +} } // same as above, but converts to RTFD - (NSData *)accessibleSelectedTextAsRTFD { - NSString *selectedText = [self accessibleSelectedText]; + NSString *selectedText = [self accessibilitySelectedText]; NSAttributedString *styledText = [[NSAttributedString alloc] initWithString:selectedText]; NSData *rtfdData = [styledText RTFDFromRange:NSMakeRange(0, [styledText length]) documentAttributes: @@ -691,8 +697,8 @@ - (NSData *)accessibleSelectedTextAsRTFD - (BOOL)replaceAccessibleTextSelection:(NSString *)text { id focused = [self accessibilityFocusedUIElement]; - if (![focused isKindOfClass:[JavaTextAccessibility class]]) return NO; - [(JavaTextAccessibility *)focused accessibilitySetSelectedTextAttribute:text]; + if (![focused respondsToSelector:@selector(setAccessibilitySelectedText)]) return NO; + [focused setAccessibilitySelectedText:text]; return YES; } @@ -702,7 +708,7 @@ - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)retu if ([[self window] firstResponder] != self) return nil; // let AWT components handle themselves if ([sendType isEqual:NSStringPboardType] || [returnType isEqual:NSStringPboardType]) { - NSString *selectedText = [self accessibleSelectedText]; + NSString *selectedText = [self accessibilitySelectedText]; if (selectedText) return self; } @@ -715,7 +721,7 @@ - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types if ([types containsObject:NSStringPboardType]) { [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - return [pboard setString:[self accessibleSelectedText] forType:NSStringPboardType]; + return [pboard setString:[self accessibilitySelectedText] forType:NSStringPboardType]; } if ([types containsObject:NSRTFDPboardType]) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h index 9c65f0d2e893d..7b575908bd460 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h @@ -62,3 +62,4 @@ void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionN void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter); BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component); NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber); +NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index 5061a35c4e9a7..19eb8c2ee8588 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -402,6 +402,22 @@ BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component) } } +/* + * Converts an int array to an NSRange wrapped inside an NSValue + * takes [start, end] values and returns [start, end - start] + */ +NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) { + jint *values = (*env)->GetIntArrayElements(env, array, 0); + if (values == NULL) { + // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. + NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__); + return nil; + }; + NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])]; + (*env)->ReleaseIntArrayElements(env, array, values, 0); + return value; +} + // end appKit copies /* diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m index 730e98e414703..4036775d27508 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaTextAccessibility.m @@ -53,23 +53,6 @@ GET_STATIC_METHOD_RETURN(sjm_getAccessibleEditableText, sjc_CAccessibleText, "getAccessibleEditableText", \ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleEditableText;", ret); - -/* - * Converts an int array to an NSRange wrapped inside an NSValue - * takes [start, end] values and returns [start, end - start] - */ -NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) { - jint *values = (*env)->GetIntArrayElements(env, array, 0); - if (values == NULL) { - // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. - NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__); - return nil; - }; - NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])]; - (*env)->ReleaseIntArrayElements(env, array, values, 0); - return value; -} - @implementation JavaTextAccessibility // based strongly upon NSTextViewAccessibility:accessibilityAttributeNames diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index ee67dc7dc40ed..3d47fc7859a63 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -112,7 +112,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:37]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:41]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -133,6 +133,10 @@ + (void) initializeRolesMap { [rolesMap setObject:@"ToolbarAccessibility" forKey:@"toolbar"]; [rolesMap setObject:@"SplitpaneAccessibility" forKey:@"splitpane"]; [rolesMap setObject:@"StatusbarAccessibility" forKey:@"statusbar"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"textarea"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"text"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"passwordtext"]; + [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"dateeditor"]; /* * All the components below should be ignored by the accessibility subsystem, diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h new file mode 100644 index 0000000000000..33a05318b5a4a --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface NavigableTextAccessibility : CommonComponentAccessibility + +@property(readonly) BOOL accessibleIsPasswordText; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m new file mode 100644 index 0000000000000..11802b6cb8e51 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "NavigableTextAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; +#define GET_CACCESSIBLITY_CLASS() \ + GET_CLASS(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility"); +#define GET_CACCESSIBLITY_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility", ret); + +static jmethodID sjm_getAccessibleText = NULL; +#define GET_ACCESSIBLETEXT_METHOD_RETURN(ret) \ + GET_CACCESSIBLITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleText, sjc_CAccessibility, "getAccessibleText", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleText;", ret); + +static jclass sjc_CAccessibleText = NULL; +#define GET_CACCESSIBLETEXT_CLASS() \ + GET_CLASS(sjc_CAccessibleText, "sun/lwawt/macosx/CAccessibleText"); +#define GET_CACCESSIBLETEXT_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessibleText, "sun/lwawt/macosx/CAccessibleText", ret); + +static jmethodID sjm_getAccessibleEditableText = NULL; +#define GET_ACCESSIBLEEDITABLETEXT_METHOD_RETURN(ret) \ + GET_CACCESSIBLETEXT_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleEditableText, sjc_CAccessibleText, "getAccessibleEditableText", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleEditableText;", ret); + + +@implementation NavigableTextAccessibility + +- (BOOL)accessibleIsPasswordText { + return [fJavaRole isEqualToString:@"passwordtext"]; +} + +// NSAccessibilityElement protocol methods + +- (NSRect)accessibilityFrameForRange:(NSRange)range +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSMakeRect(0, 0, 0, 0)); + DECLARE_STATIC_METHOD_RETURN(jm_getBoundsForRange, sjc_CAccessibleText, "getBoundsForRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)[D", NSMakeRect(0, 0, 0, 0)); + jdoubleArray axBounds = (jdoubleArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getBoundsForRange, + fAccessible, fComponent, range.location, range.length); + CHECK_EXCEPTION(); + if (axBounds == NULL) return NSMakeRect(0, 0, 0, 0); + + // We cheat because we know that the array is 4 elements long (x, y, width, height) + jdouble *values = (*env)->GetDoubleArrayElements(env, axBounds, 0); + CHECK_EXCEPTION(); + if (values == NULL) { + // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. + NSLog(@"%s failed calling GetDoubleArrayElements", __FUNCTION__); + return NSMakeRect(0, 0, 0, 0); + }; + NSRect bounds; + bounds.origin.x = values[0]; + bounds.origin.y = [[[[self view] window] screen] frame].size.height - values[1] - values[3]; //values[1] is y-coord from top-left of screen. Flip. Account for the height (values[3]) when flipping + bounds.size.width = values[2]; + bounds.size.height = values[3]; + (*env)->ReleaseDoubleArrayElements(env, axBounds, values, 0); + return bounds; +} + +- (NSInteger)accessibilityLineForIndex:(NSInteger)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(-1); + DECLARE_STATIC_METHOD_RETURN(jm_getLineNumberForIndex, sjc_CAccessibleText, "getLineNumberForIndex", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)I", -1); + jint row = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, jm_getLineNumberForIndex, + fAccessible, fComponent, index); + CHECK_EXCEPTION(); + if (row < 0) return -1; + return row; +} + +- (NSRange)accessibilityRangeForLine:(NSInteger)line +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getRangeForLine, sjc_CAccessibleText, "getRangeForLine", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", NSRangeFromString(@"")); + jintArray axTextRange = (jintArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + jm_getRangeForLine, fAccessible, fComponent, line); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + NSRange range = [javaIntArrayToNSRangeValue(env,axTextRange) rangeValue]; + (*env)->DeleteLocalRef(env, axTextRange); + return range; +} + +- (NSString *)accessibilityStringForRange:(NSRange)range +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getStringForRange, sjc_CAccessibleText, "getStringForRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)Ljava/lang/String;", nil); + jstring jstringForRange = (jstring)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getStringForRange, + fAccessible, fComponent, range.location, range.length); + CHECK_EXCEPTION(); + if (jstringForRange == NULL) return @""; + NSString* str = JavaStringToNSString(env, jstringForRange); + (*env)->DeleteLocalRef(env, jstringForRange); + return str; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + // cmcnote: inefficient to make three distinct JNI calls. Coalesce. radr://3951923 + GET_ACCESSIBLETEXT_METHOD_RETURN(@""); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleText, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axText == NULL) return nil; + (*env)->DeleteLocalRef(env, axText); + + GET_ACCESSIBLEEDITABLETEXT_METHOD_RETURN(nil); + jobject axEditableText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + sjm_getAccessibleEditableText, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axEditableText == NULL) return nil; + + DECLARE_STATIC_METHOD_RETURN(jm_getTextRange, sjc_CAccessibleText, "getTextRange", + "(Ljavax/accessibility/AccessibleEditableText;IILjava/awt/Component;)Ljava/lang/String;", nil); + jobject jrange = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getTextRange, + axEditableText, 0, getAxTextCharCount(env, axEditableText, fComponent), fComponent); + CHECK_EXCEPTION(); + NSString *string = JavaStringToNSString(env, jrange); + + (*env)->DeleteLocalRef(env, jrange); + (*env)->DeleteLocalRef(env, axEditableText); + + if (string == nil) string = @""; + return string; +} + +- (NSAccessibilitySubrole)accessibilitySubrole { + if ([self accessibleIsPasswordText]) { + return NSAccessibilitySecureTextFieldSubrole; + } + return nil; +} + +- (NSRange)accessibilityRangeForIndex:(NSInteger)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getRangeForIndex, sjc_CAccessibleText, "getRangeForIndex", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", NSRangeFromString(@"")); + jintArray axTextRange = (jintArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getRangeForIndex, + fAccessible, fComponent, index); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + return [javaIntArrayToNSRangeValue(env, axTextRange) rangeValue]; +} + +- (NSAccessibilityRole)accessibilityRole { + return [sRoles objectForKey:self.javaRole]; +} + +- (NSRange)accessibilityRangeForPosition:(NSPoint)point +{ + point.y = [[[[self view] window] screen] frame].size.height - point.y; // flip into java screen coords (0 is at upper-left corner of screen) + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getCharacterIndexAtPosition, sjc_CAccessibleText, "getCharacterIndexAtPosition", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)I", NSRangeFromString(@"")); + jint charIndex = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, jm_getCharacterIndexAtPosition, + fAccessible, fComponent, point.x, point.y); + CHECK_EXCEPTION(); + if (charIndex == -1) return NSRangeFromString(@""); + + // AccessibleText.getIndexAtPoint returns -1 for an invalid point + NSRange range = NSMakeRange(charIndex, 1); //range's length is 1 - one-character range + return range; +} + +- (NSString *)accessibilitySelectedText +{ + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getSelectedText, sjc_CAccessibleText, "getSelectedText", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", nil); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, jm_getSelectedText, + fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axText == NULL) return @""; + NSString* str = JavaStringToNSString(env, axText); + (*env)->DeleteLocalRef(env, axText); + return str; +} + +- (NSRange)accessibilitySelectedTextRange +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(NSRangeFromString(@"")); + DECLARE_STATIC_METHOD_RETURN(jm_getSelectedTextRange, sjc_CAccessibleText, "getSelectedTextRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[I", NSRangeFromString(@"")); + jintArray axTextRange = (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText, + jm_getSelectedTextRange, fAccessible, fComponent); + CHECK_EXCEPTION(); + if (axTextRange == NULL) return NSRangeFromString(@""); + + return [javaIntArrayToNSRangeValue(env, axTextRange) rangeValue]; +} + +- (NSInteger)accessibilityNumberOfCharacters +{ + // cmcnote: should coalesce these two calls - radr://3951923 + // also, static text doesn't always have accessibleText. if axText is null, should get the charcount of the accessibleName instead + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ACCESSIBLETEXT_METHOD_RETURN(0); + jobject axText = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getAccessibleText, fAccessible, fComponent); + CHECK_EXCEPTION(); + NSInteger num = getAxTextCharCount(env, axText, fComponent); + (*env)->DeleteLocalRef(env, axText); + return num; +} + +- (NSInteger)accessibilityInsertionPointLineNumber +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS_RETURN(0); + DECLARE_STATIC_METHOD_RETURN(jm_getLineNumberForInsertionPoint, sjc_CAccessibleText, + "getLineNumberForInsertionPoint", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)I", 0); + jint row = (*env)->CallStaticIntMethod(env, sjc_CAccessibleText, + jm_getLineNumberForInsertionPoint, fAccessible, fComponent); + CHECK_EXCEPTION(); + return row >= 0 ? row : 0; +} + +- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jstring jstringValue = NSStringToJavaString(env, accessibilitySelectedText); + GET_CACCESSIBLETEXT_CLASS(); + DECLARE_STATIC_METHOD(jm_setSelectedText, sjc_CAccessibleText, "setSelectedText", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;Ljava/lang/String;)V"); + (*env)->CallStaticVoidMethod(env, sjc_CAccessibleText, jm_setSelectedText, + fAccessible, fComponent, jstringValue); + CHECK_EXCEPTION(); +} + +- (void)setAccessibilitySelectedTextRange:(NSRange)accessibilitySelectedTextRange +{ + jint startIndex = accessibilitySelectedTextRange.location; + jint endIndex = startIndex + accessibilitySelectedTextRange.length; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBLETEXT_CLASS(); + DECLARE_STATIC_METHOD(jm_setSelectedTextRange, sjc_CAccessibleText, "setSelectedTextRange", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;II)V"); + (*env)->CallStaticVoidMethod(env, sjc_CAccessibleText, jm_setSelectedTextRange, + fAccessible, fComponent, startIndex, endIndex); + CHECK_EXCEPTION(); +} + +- (BOOL)isAccessibilityEdited { + return YES; +} + +- (BOOL)isAccessibilityEnabled { + return YES; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +/* +* Other text methods +- (NSRange)accessibilitySharedCharacterRange; +- (NSArray *)accessibilitySharedTextUIElements; +- (NSData *)accessibilityRTFForRange:(NSRange)range; +- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index; +*/ + +@end + diff --git a/test/jdk/java/awt/a11y/AccessibleTextTest.java b/test/jdk/java/awt/a11y/AccessibleTextTest.java new file mode 100644 index 0000000000000..f2e96c2b02920 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleTextTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8262031 + * @summary Create implementation for NSAccessibilityNavigableStaticText protocol + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleTextTest + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyAdapter; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleTextTest extends AccessibleComponentTest { + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + private void createSimpleLabel() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JLabel.\n\n" + + "Turn screen reader on.\n" + + "On MacOS, use the VO navigation keys to read the label text;\n" + + "ON Windows with JAWS, use window virtualization (insert+alt+w and arrows) to read the label text;\n" + + "ON Windows with NVDA, use the browse cursor (insert+num4 or insert+num6) to read the label text;\n\n" + + "If you can hear text from label tab further and press PASS, otherwise press FAIL."; + + JLabel label = new JLabel("this is a label"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(label); + exceptionString = "Simple label test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createOneLineTexField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTextField.\n\n" + + "Turn screen reader on and press Tab to move to the text field and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextField textField = new JTextField("some text to edit"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textField); + exceptionString = "Simple text field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createPassField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JPasswordField.\n\n" + + "Turn screen reader on and press Tab to move to the password field and type some characters.\n\n" + + "If you can hear sounds specific to your screen reader when interacting with password fields, tab further and press PASS, otherwise press FAIL."; + + JPasswordField passwordField = new JPasswordField("12345678"); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(passwordField); + exceptionString = "Simple passfield field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createNamedTextField() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTextField.\n\n" + + "Turn screen reader on and press Tab to move to the text field.\n\n" + + "If you can hear in addition to the fact that this text field is also the names of these fields, tab further and press PASS, otherwise press FAIL."; + + JTextField textField = new JTextField("some text 1"); + textField.getAccessibleContext().setAccessibleName("This is the first text field:"); + + JLabel label = new JLabel("This is the second text field:"); + JTextField secondTextField = new JTextField("some text 2"); + label.setLabelFor(secondTextField); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textField); + panel.add(label); + panel.add(secondTextField); + exceptionString = "Named text field test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createTextArea() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTextArea.\n\n" + + "Turn screen reader on and press the arrow keys.\n\n" + + "If you can hear this instructions, tab further and press PASS, otherwise press FAIL."; + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + exceptionString = "Simple text area test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createEditableTextArea() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of editable JTextArea.\n\n" + + "Turn screen reader on and press Tab to move to the text area and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextArea textArea = new JTextArea("some text to edit"); + JLabel label = new JLabel(textArea.getText().length() + " chars"); + label.setLabelFor(textArea); + textArea.setEditable(true); + textArea.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + label.setText(String.valueOf(textArea.getText().length()) + " chars"); + } + }); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textArea); + panel.add(label); + exceptionString = "Editable text area test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of text in JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and press the arrow keys.\n\n" + + "If you can hear text, tab further and press PASS, otherwise press FAIL."; + + String str = "Line 1\nLine 2\nLine 3"; + JTextPane textPane = new JTextPane(); + textPane.setEditable(false); + textPane.setText(str); + JTextArea textArea = new JTextArea(); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + exceptionString = "Simple text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createHTMLTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of html text in JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and press the arrow keys.\n\n" + + "If you can hear text, tab further and press PASS, otherwise press FAIL."; + + String str = "

Header

  • Item 1
  • Item 2
  • Item 3
"; + JTextPane textPane = new JTextPane(); + textPane.setEditable(false); + textPane.setContentType("text/html"); + textPane.setText(str); + JTextArea textArea = new JTextArea(); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + exceptionString = "HTML text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + private void createEditableTextPane() { + AccessibleComponentTest.INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of editable JTextPane.\n\n" + + "Turn screen reader on and press Tab to move to the text pane and type some characters.\n\n" + + "If you can hear input results according to your screen reader settings, tab further and press PASS, otherwise press FAIL."; + + JTextPane textPane = new JTextPane(); + textPane.setText("some text to edit"); + JLabel label = new JLabel(textPane.getText().length() + " chars"); + label.setLabelFor(textPane); + textPane.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + label.setText(String.valueOf(textPane.getText().length()) + " chars"); + } + }); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(textPane); + panel.add(label); + exceptionString = "Editable text pane test failed!"; + super.createUI(panel, "AccessibleTextTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleTextTest test = new AccessibleTextTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSimpleLabel); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createOneLineTexField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createPassField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createNamedTextField); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createTextArea); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createEditableTextArea); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createHTMLTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createEditableTextPane); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + } +} From f7979c621b46ef0aeed4489760304f57e5d864e1 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 14:50:46 +0300 Subject: [PATCH 03/15] 8264287: Create implementation for NSAccessibilityComboBox protocol peer --- .../awt/a11y/ComboBoxAccessibility.h | 28 +++++++ .../awt/a11y/ComboBoxAccessibility.m | 66 ++++++++++++++++ .../awt/a11y/CommonComponentAccessibility.m | 3 +- .../awt/a11y/AccessibleJComboboxTest.java | 76 +++++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleJComboboxTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h new file mode 100644 index 0000000000000..4a39938a826fa --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ComboBoxAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m new file mode 100644 index 0000000000000..4f9b65d1dcf70 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ComboBoxAccessibility.h" +#import "../JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); + +@implementation ComboBoxAccessibility + +// NSAccessibilityElement protocol methods + +- (id)accessibilityValue { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return nil; + jclass axContextClass = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axContextClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil); + jobject axSelectedChild = (*env)->CallObjectMethod(env, axContext, jm_getAccessibleSelection, 0); + (*env)->DeleteLocalRef(env, axContext); + if (axSelectedChild == NULL) { + return nil; + } + GET_CACCESSIBILITY_CLASS_RETURN(nil); + GET_ACCESSIBLENAME_METHOD_RETURN(nil); + jobject childName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, axSelectedChild, fComponent); + if (childName == NULL) { + (*env)->DeleteLocalRef(env, axSelectedChild); + return nil; + } + NSString *selectedText = JavaStringToNSString(env, childName); + (*env)->DeleteLocalRef(env, axSelectedChild); + (*env)->DeleteLocalRef(env, childName); + return selectedText; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 3d47fc7859a63..6bd13adfef3b5 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -112,7 +112,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:41]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:42]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -137,6 +137,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"text"]; [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"passwordtext"]; [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"dateeditor"]; + [rolesMap setObject:@"ComboBoxAccessibility" forKey:@"combobox"]; /* * All the components below should be ignored by the accessibility subsystem, diff --git a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java new file mode 100644 index 0000000000000..f74aaaba3beee --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264287 + * @summary Create implementation for NSAccessibilityComboBox protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJComboboxTest + */ + +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.CountDownLatch; + +public class AccessibleJComboboxTest extends AccessibleComponentTest { + + @java.lang.Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + void createCombobox() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JCombobox.\n\n" + + "Turn screen reader on, and Tab to the combobox.\n\n" + + "If you can hear combobox selected item tab further and press PASS, otherwise press FAIL."; + + JPanel frame = new JPanel(); + + String[] NAMES = {"One", "Two", "Three", "Four", "Five"}; + JComboBox combo = new JComboBox<>(NAMES); + + JLabel label = new JLabel("This is combobox:"); + label.setLabelFor(combo); + + frame.setLayout(new FlowLayout()); + frame.add(label); + frame.add(combo); + exceptionString = "AccessibleJCombobox test failed!"; + super.createUI(frame, "AccessibleJComboboxTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJComboboxTest test = new AccessibleJComboboxTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeLater(test::createCombobox); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } +} From e8b284fd53f88c205d5818d0458b34da910830a5 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 14:58:23 +0300 Subject: [PATCH 04/15] 8264303: Create implementation for NSAccessibilityTabGroup protocol peer --- .../awt/a11y/CommonComponentAccessibility.m | 3 +- .../awt/a11y/TabButtonAccessibility.h | 37 ++++ .../awt/a11y/TabButtonAccessibility.m | 107 +++++++++ .../awt/a11y/TabGroupAccessibility.h | 37 ++++ .../awt/a11y/TabGroupAccessibility.m | 203 ++++++++++++++++++ .../awt/a11y/AccessibleJTabbedPaneTest.java | 85 ++++++++ 6 files changed, 471 insertions(+), 1 deletion(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 6bd13adfef3b5..fda984892a15d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -112,7 +112,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:42]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:43]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -138,6 +138,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"passwordtext"]; [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"dateeditor"]; [rolesMap setObject:@"ComboBoxAccessibility" forKey:@"combobox"]; + [rolesMap setObject:@"TabGroupAccessibility" forKey:@"pagetablist"]; /* * All the components below should be ignored by the accessibility subsystem, diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h new file mode 100644 index 0000000000000..0b497d4afc264 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TabButtonAccessibility : CommonComponentAccessibility { + jobject fTabGroupAxContext; +} + +@property(readonly) jobject tabGroup; + +// from TabGroup controller +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole; +- (void)performPressAction; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m new file mode 100644 index 0000000000000..4caf97c21cb67 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TabButtonAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +@implementation TabButtonAccessibility + +- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole +{ + self = [super initWithParent:parent withEnv:env withAccessible:accessible withIndex:index withView:view withJavaRole:javaRole]; + if (self) { + if (tabGroup != NULL) { + fTabGroupAxContext = (*env)->NewWeakGlobalRef(env, tabGroup); + CHECK_EXCEPTION(); + } else { + fTabGroupAxContext = NULL; + } + } + return self; +} + +- (void)dealloc +{ + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; + + if (fTabGroupAxContext != NULL) { + (*env)->DeleteWeakGlobalRef(env, fTabGroupAxContext); + fTabGroupAxContext = NULL; + } + + [super dealloc]; +} + +- (jobject)tabGroup +{ + if (fTabGroupAxContext == NULL) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + jobject tabGroupAxContext = [(CommonComponentAccessibility *)[self parent] axContextWithEnv:env]; + fTabGroupAxContext = (*env)->NewWeakGlobalRef(env, tabGroupAxContext); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, tabGroupAxContext); + } + return fTabGroupAxContext; +} + +- (void)performPressAction { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + TabGroupAction *action = [[TabGroupAction alloc] initWithEnv:env withTabGroup:[self tabGroup] withIndex:fIndex withComponent:fComponent]; + [action perform]; + [action release]; +} + +// NSAccessibilityElement protocol methods + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + if (@available(macOS 10.13, *)) { + return NSAccessibilityTabButtonSubrole; + } + return NSAccessibilityUnknownSubrole; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + jobject selAccessible = getAxContextSelection(env, [self tabGroup], fIndex, fComponent); + + // Returns the current selection of the page tab list + id val = [NSNumber numberWithBool:ObjectEquals(env, axContext, selAccessible, fComponent)]; + + (*env)->DeleteLocalRef(env, selAccessible); + (*env)->DeleteLocalRef(env, axContext); + return val; +} + +- (BOOL)accessibilityPerformPress { + [self performPressAction]; + return YES; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h new file mode 100644 index 0000000000000..c99b18c904e95 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TabGroupAccessibility : CommonComponentAccessibility { + NSInteger _numTabs; +} + +@property(readonly) NSInteger numTabs; + +- (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext; +- (NSArray *)tabButtonsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; +- (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m new file mode 100644 index 0000000000000..822bec2db3c09 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TabGroupAccessibility.h" +#import "TabButtonAccessibility.h" +#import "../JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation TabGroupAccessibility + +- (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext +{ + NSArray *tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + + // Looking at the JTabbedPane sources, there is always one AccessibleSelection. + jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent); + if (selAccessible == NULL) return nil; + + // Go through the tabs and find selAccessible + _numTabs = [tabs count]; + CommonComponentAccessibility *aTab; + NSInteger i; + for (i = 0; i < _numTabs; i++) { + aTab = (CommonComponentAccessibility *)[tabs objectAtIndex:i]; + if ([aTab isAccessibleWithEnv:env forAccessible:selAccessible]) { + (*env)->DeleteLocalRef(env, selAccessible); + return aTab; + } + } + (*env)->DeleteLocalRef(env, selAccessible); + return nil; +} + +- (NSArray *)tabButtonsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored +{ + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jtabsAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + fAccessible, fComponent, whichTabs, allowIgnored); + CHECK_EXCEPTION(); + if(jtabsAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jtabsAndRoles); + if (arrayLen == 0) { + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return nil; + } + NSMutableArray *tabs = [NSMutableArray arrayWithCapacity:(arrayLen/2)]; + + // 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 + jobject jtabJavaRole = (*env)->GetObjectArrayElement(env, jtabsAndRoles, 1); // the array entries alternate between tab/role, starting with tab. so the first role is entry 1. + if (jtabJavaRole == NULL) { + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return nil; + } + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jtabJavaRole, sjf_key); + CHECK_EXCEPTION(); + NSString *tabJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + + NSInteger i; + NSUInteger tabIndex = (whichTabs >= 0) ? whichTabs : 0; // if we're getting one particular child, make sure to set its index correctly + for(i = 0; i < arrayLen; i+=2) { + jobject jtab = (*env)->GetObjectArrayElement(env, jtabsAndRoles, i); + CommonComponentAccessibility *tab = [[[TabButtonAccessibility alloc] initWithParent:self withEnv:env withAccessible:jtab withIndex:tabIndex withTabGroup:axContext withView:[self view] withJavaRole:tabJavaRole] autorelease]; + (*env)->DeleteLocalRef(env, jtab); + [tabs addObject:tab]; + tabIndex++; + } + (*env)->DeleteLocalRef(env, jtabsAndRoles); + return tabs; +} + +- (NSArray *)contentsWithEnv:(JNIEnv *)env withTabGroupAxContext:(jobject)axContext withTabCode:(NSInteger)whichTabs allowIgnored:(BOOL)allowIgnored +{ + // Contents are the children of the selected tab. + id currentTab = [self currentTabWithEnv:env withAxContext:axContext]; + if (currentTab == nil) return nil; + + NSArray *contents = [CommonComponentAccessibility childrenOfParent:currentTab withEnv:env withChildrenCode:whichTabs allowIgnored:allowIgnored]; + if ([contents count] <= 0) return nil; + return contents; +} + +- (NSInteger)numTabs +{ + if (_numTabs == -1) { + _numTabs = [[self accessibilityTabsAttribute] count]; + } + return _numTabs; +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityTabs +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + id tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + (*env)->DeleteLocalRef(env, axContext); + return tabs; +} + +- (NSArray *)accessibilityContents +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + (*env)->DeleteLocalRef(env, axContext); + return cont; +} + +- (id)accessibilityValue +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + id val = [self currentTabWithEnv:env withAxContext:axContext]; + (*env)->DeleteLocalRef(env, axContext); + return val; +} + +- (NSArray *)accessibilityChildren +{ + //children = AXTabs + AXContents + NSArray *tabs = [self accessibilityTabs]; + NSArray *contents = [self accessibilityContents]; + + NSMutableArray *children = [NSMutableArray arrayWithCapacity:[tabs count] + [contents count]]; + [children addObjectsFromArray:tabs]; + [children addObjectsFromArray:contents]; + + return (NSArray *)children; +} + +- (NSArray *)accessibilityArrayAttributeValues:(NSAccessibilityAttributeName)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount +{ + NSArray *result = nil; + if ( (maxCount == 1) && [attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + // Children codes for ALL, SELECTED, VISIBLE are <0. If the code is >=0, we treat it as an index to a single child + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + + //children = AXTabs + AXContents + NSArray *children = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:index allowIgnored:NO]; // first look at the tabs + if ([children count] > 0) { + result = children; + } else { + children= [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:(index-[self numTabs]) allowIgnored:NO]; + if ([children count] > 0) { + result = children; + } + } + (*env)->DeleteLocalRef(env, axContext); + } else { + result = [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; + } + return result; +} + +- (void)setAccessibilityValue:(id)accessibilityValue +{ + // set the current tab + NSNumber *number = (NSNumber *)accessibilityValue; + if (![number boolValue]) return; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + setAxContextSelection(env, axContext, fIndex, fComponent); + (*env)->DeleteLocalRef(env, axContext); +} + +@end diff --git a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java new file mode 100644 index 0000000000000..70d9ffd5b339e --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264303 + * @summary Create implementation for NSAccessibilityTabGroup protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTabbedPaneTest + */ + +import javax.swing.*; +import java.util.concurrent.CountDownLatch; + +public class AccessibleJTabbedPaneTest extends AccessibleComponentTest { + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + void createTabPane() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTabbedPane.\n\n" + + "Turn screen reader on, and tab to the JTabbedPane.\n" + + "Use page up and page down arrow buttons to move through the tabs.\n\n" + + "If you can hear selected tab names tab further and press PASS, otherwise press FAIL.\n"; + + JTabbedPane tabbedPane = new JTabbedPane(); + + JPanel panel1 = new JPanel(); + String[] names = {"One", "Two", "Three", "Four", "Five"}; + JList list = new JList(names); + JLabel fieldName = new JLabel("Text field:"); + JTextField textField = new JTextField("some text"); + fieldName.setLabelFor(textField); + panel1.add(fieldName); + panel1.add(textField); + panel1.add(list); + tabbedPane.addTab("Tab 1", panel1); + JPanel panel2 = new JPanel(); + for (int i = 0; i < 5; i++) { + panel2.add(new JCheckBox("CheckBox " + String.valueOf(i + 1))); + } + tabbedPane.addTab("tab 2", panel2); + JPanel panel = new JPanel(); + panel.add(tabbedPane); + + exceptionString = "AccessibleJTabbedPane test failed!"; + createUI(panel, "AccessibleJTabbedPaneTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTabbedPaneTest test = new AccessibleJTabbedPaneTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeLater(test::createTabPane); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } +} From dd219ab6edc37b1476e43ce42f6da2910e2a858b Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 15:10:43 +0300 Subject: [PATCH 05/15] 8264292: Create implementation for NSAccessibilityList protocol peer --- .../sun/lwawt/macosx/CAccessibility.java | 5 + .../awt/a11y/CommonComponentAccessibility.h | 6 + .../awt/a11y/CommonComponentAccessibility.m | 47 ++- .../libawt_lwawt/awt/a11y/ListAccessibility.h | 28 ++ .../libawt_lwawt/awt/a11y/ListAccessibility.m | 63 ++++ .../awt/a11y/ListRowAccessibility.h | 28 ++ .../awt/a11y/ListRowAccessibility.m | 73 ++++ .../java/awt/a11y/AccessibleJListTest.java | 312 ++++++++++++++++++ 8 files changed, 553 insertions(+), 9 deletions(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleJListTest.java diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index b52be53385784..771c0beafbb5f 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -57,6 +57,7 @@ import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JTextArea; +import javax.swing.JList; import javax.swing.KeyStroke; import sun.awt.AWTAccessor; @@ -551,6 +552,10 @@ public void run() { if (pac == null) return; AccessibleSelection as = pac.getAccessibleSelection(); if (as == null) return; + if (parent instanceof JList) { + ((JList) parent).setSelectedIndex(i); + return; + } as.addAccessibleSelection(i); } }, c); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index 82981bd219d9a..b52b0bc4c282d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -69,12 +69,18 @@ + (void) initializeRolesMap; + (CommonComponentAccessibility* _Nullable) getComponentAccessibility:(NSString* _Nonnull)role; ++ (CommonComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role andParent:(CommonComponentAccessibility * _Nonnull)parent; + (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored; + (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)role index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; +// If the isWraped parameter is true, then the object passed as a parent was created based on the same java component, +// but performs a different NSAccessibilityRole of a table cell, or a list row, or tree row, +// and we need to create an element whose role corresponds to the role in Java. ++ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isWrapped:(BOOL)wrapped; + - (jobject _Nullable)axContextWithEnv:(JNIEnv _Nonnull * _Nonnull)env; - (NSView* _Nonnull)view; - (NSWindow* _Nonnull)window; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index fda984892a15d..e5b842d97755c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -65,6 +65,7 @@ GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret); static NSMutableDictionary * _Nullable rolesMap; +static NSMutableDictionary * _Nullable rowRolesMapForParent; NSString *const IgnoreClassName = @"IgnoreAccessibility"; static jobject sAccessibilityClass = NULL; @@ -112,7 +113,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:43]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:44]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -139,6 +140,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"NavigableTextAccessibility" forKey:@"dateeditor"]; [rolesMap setObject:@"ComboBoxAccessibility" forKey:@"combobox"]; [rolesMap setObject:@"TabGroupAccessibility" forKey:@"pagetablist"]; + [rolesMap setObject:@"ListAccessibility" forKey:@"list"]; /* * All the components below should be ignored by the accessibility subsystem, @@ -164,6 +166,10 @@ + (void) initializeRolesMap { [rolesMap setObject:IgnoreClassName forKey:@"viewport"]; [rolesMap setObject:IgnoreClassName forKey:@"window"]; + rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:1]; + + [rowRolesMapForParent setObject:@"ListRowAccessibility" forKey:@"ListAccessibility"]; + /* * Initialize CAccessibility instance */ @@ -216,6 +222,19 @@ + (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role return [CommonComponentAccessibility alloc]; } ++ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role andParent:(CommonComponentAccessibility *)parent +{ + AWT_ASSERT_APPKIT_THREAD; + if (rolesMap == nil) { + [self initializeRolesMap]; + } + NSString *className = [rowRolesMapForParent objectForKey:[[parent class] className]]; + if (className == nil) { + return [CommonComponentAccessibility getComponentAccessibility:role]; + } + return [NSClassFromString(className) alloc]; +} + - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole { self = [super init]; @@ -436,22 +455,28 @@ + (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible rol { return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; } - + (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [CommonComponentAccessibility createWithParent:parent accessible:jaccessible role:javaRole index:index withEnv:env withView:view isWrapped:NO]; +} + ++ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view isWrapped:(BOOL)wrapped { GET_CACCESSIBLE_CLASS_RETURN(NULL); DECLARE_FIELD_RETURN(jf_ptr, sjc_CAccessible, "ptr", "J", NULL); // try to fetch the jCAX from Java, and return autoreleased jobject jCAX = [CommonComponentAccessibility getCAccessible:jaccessible withEnv:env]; if (jCAX == NULL) return nil; - CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); - if (value != nil) { - (*env)->DeleteLocalRef(env, jCAX); - return [[value retain] autorelease]; + if (!wrapped) { // If wrapped is true, then you don't need to get an existing instance, you need to create a new one + CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); + if (value != nil) { + (*env)->DeleteLocalRef(env, jCAX); + return [[value retain] autorelease]; + } } // otherwise, create a new instance - CommonComponentAccessibility *newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole];; + CommonComponentAccessibility *newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole andParent:parent]; // must init freshly -alloc'd object [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance @@ -467,7 +492,11 @@ + (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibilit // must hard retain pointer poked into Java object [newChild retain]; (*env)->SetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); - (*env)->DeleteLocalRef(env, jCAX); + + // the link is removed in the wrapper + if (!wrapped) { + (*env)->DeleteLocalRef(env, jCAX); + } // return autoreleased instance return [newChild autorelease]; @@ -545,7 +574,7 @@ - (NSArray *)accessibleChildrenWithChildCode:(NSInteger)childCode NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:childCode - allowIgnored:NO]; + allowIgnored:[[self accessibilityRole] isEqualToString:NSAccessibilityListRole]]; NSArray *value = nil; if ([children count] > 0) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h new file mode 100644 index 0000000000000..ddfcd2217f8cb --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ListAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m new file mode 100644 index 0000000000000..4dc958886bb21 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListAccessibility.m @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" + +@implementation ListAccessibility + +// NSAccessibilityElement protocol methods + +- (nullable NSArray> *)accessibilityRows +{ + return [self accessibilityChildren]; +} + +- (nullable NSArray> *)accessibilitySelectedRows +{ + return [self accessibilitySelectedChildren]; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel] == NULL ? @"list" : [super accessibilityLabel]; +} + +// to avoid warning (why?): method in protocol 'NSAccessibilityElement' not implemented + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +// to avoid warning (why?): method in protocol 'NSAccessibilityElement' not implemented + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +@end + diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h new file mode 100644 index 0000000000000..2453f92e58fe0 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ListRowAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m new file mode 100644 index 0000000000000..6dbb0b95cba4e --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "ListRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "ListAccessibility.h" +#import "ThreadUtilities.h" + +@implementation ListRowAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:self->fJavaRole + index:self->fIndex + withEnv:[ThreadUtilities getJNIEnv] + withView:self->fView + isWrapped:YES]; + return [NSArray arrayWithObject:[newChild autorelease]]; + } else { + return children; + } +} + +- (NSInteger)accessibilityIndex +{ + return [[self accessibilityParent] accessibilityIndexOfChild:self]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +@end diff --git a/test/jdk/java/awt/a11y/AccessibleJListTest.java b/test/jdk/java/awt/a11y/AccessibleJListTest.java new file mode 100644 index 0000000000000..c6f093c755576 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJListTest.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8264292 + * @summary Create implementation for NSAccessibilityList protocol peer + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJListTest + */ + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.awt.*; + +public class AccessibleJListTest extends AccessibleComponentTest { + + private static final String[] NAMES = {"One", "Two", "Three", "Four", "Five"}; + static JWindow window; + + public static void main(String[] args) throws Exception { + a11yTest = new AccessibleJListTest(); + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleList); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleListRenderer); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createSimpleListNamed); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createCombobox); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createPushButton); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + + countDownLatch = a11yTest.createCountDownLatch(); + SwingUtilities.invokeLater(((AccessibleJListTest) a11yTest)::createPushButtonHeavyWeight); + countDownLatch.await(); + + if (!testResult) { + throw new RuntimeException(a11yTest.exceptionString); + } + } + + @java.lang.Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createSimpleList() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + + frame.setLayout(new FlowLayout()); + frame.add(list); + exceptionString = "Accessible JList simple list test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createSimpleListRenderer() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList with renderer.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + list.setCellRenderer(new AccessibleJListTestRenderer()); + + frame.setLayout(new FlowLayout()); + frame.add(list); + exceptionString = "Accessible JList with renderer simple list test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createSimpleListNamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JList.\n\n" + + "Turn screen reader on, and Tab to the list.\n" + + "Press the tab button to move to second list.\n\n" + + "If you can hear second list name: \"second list\" - tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JList list = new JList<>(NAMES); + JList secondList = new JList<>(NAMES); + secondList.getAccessibleContext().setAccessibleName("Second list"); + frame.setLayout(new FlowLayout()); + frame.add(list); + frame.add(secondList); + exceptionString = "Accessible JList simple list named test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + + public void createCombobox() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a combobox.\n\n" + + "Turn screen reader on, and Tab to the combobox.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear combobox items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JComboBox combo = new JComboBox<>(NAMES); + + frame.setLayout(new FlowLayout()); + frame.add(combo); + exceptionString = "Accessible JList combobox test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createPushButton() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a popup in a simple window.\n\n" + + "Turn screen reader on, and Tab to the show button and press space.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear popup menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JButton button = new JButton("show"); + button.setPreferredSize(new Dimension(100, 35)); + + button.addActionListener(new ActionListener() { + + final Runnable dispose = () -> { + window.dispose(); + window = null; + button.setText("show"); + }; + + @Override + public void actionPerformed(ActionEvent e) { + if (window == null) { + Rectangle bounds = frame.getBounds(); + window = new JWindow(mainFrame); + JList winList = new JList<>(NAMES); + winList.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + dispose.run(); + } + } + }); + window.add(winList); + window.setLocation(bounds.x + bounds.width + 20, bounds.y); + window.pack(); + window.setVisible(true); + button.setText("hide (ESC)"); + } else { + dispose.run(); + } + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(button); + exceptionString = "Accessible JList push button with simple window test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public void createPushButtonHeavyWeight() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JList in a popup in a heavy weight window.\n\n" + + "Turn screen reader on, and Tab to the show button and press space.\n" + + "Press the up and down arrow buttons to move through the list.\n\n" + + "If you can hear popup menu items tab further and press PASS, otherwise press FAIL.\n"; + + JPanel frame = new JPanel(); + + JButton button = new JButton("show"); + button.setPreferredSize(new Dimension(100, 35)); + + button.addActionListener(new ActionListener() { + private Popup popup = null; + + final Runnable dispose = () -> { + popup.hide(); + popup = null; + button.requestFocus(); + button.setText("show"); + }; + + @Override + public void actionPerformed(ActionEvent e) { + if (popup == null) { + Rectangle bounds = frame.getBounds(); + PopupFactory factory = PopupFactory.getSharedInstance(); + JList winList = new JList<>(NAMES); + winList.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + dispose.run(); + } + } + }); + popup = factory.getPopup(frame, winList, bounds.x + bounds.width + 20, bounds.y); + Window c = SwingUtilities.getWindowAncestor(winList); + if (c != null) { + c.setAutoRequestFocus(true); + c.setFocusableWindowState(true); + } + popup.show(); + button.setText("hide (ESC)"); + } else { + dispose.run(); + } + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(button); + exceptionString = "Accessible JList push button with heavy weight window test failed!"; + super.createUI(frame, "Accessible JList test"); + } + + public static class AccessibleJListTestRenderer extends JPanel implements ListCellRenderer { + private JLabel labelAJT = new JLabel("AJL"); + private JLabel itemName = new JLabel(); + + AccessibleJListTestRenderer() { + super(new FlowLayout()); + setFocusable(false); + layoutComponents(); + } + + private void layoutComponents() { + add(labelAJT); + add(itemName); + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(Math.min(size.width, 245), size.height); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + itemName.setText(((String) value)); + + getAccessibleContext().setAccessibleName(labelAJT.getText() + ", " + itemName.getText()); + return this; + } + } +} From d74af05deab9184a29312a29210ab2e8d21a6abf Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 15:22:30 +0300 Subject: [PATCH 06/15] 8267387: Create implementation for NSAccessibilityOutline protocol --- .../sun/lwawt/macosx/CAccessibility.java | 52 +++- .../classes/sun/lwawt/macosx/CAccessible.java | 13 +- .../awt/JavaAccessibilityUtilities.h | 1 + .../awt/JavaAccessibilityUtilities.m | 16 +- .../awt/a11y/CommonComponentAccessibility.h | 7 + .../awt/a11y/CommonComponentAccessibility.m | 107 +++++++- .../awt/a11y/OutlineAccessibility.h | 33 +++ .../awt/a11y/OutlineAccessibility.m | 54 ++++ .../awt/a11y/OutlineRowAccessibility.h | 33 +++ .../awt/a11y/OutlineRowAccessibility.m | 108 ++++++++ .../java/awt/a11y/AccessibleJTreeTest.java | 231 ++++++++++++++++++ 11 files changed, 633 insertions(+), 22 deletions(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleJTreeTest.java diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index 771c0beafbb5f..aae83e76fc735 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -32,6 +32,7 @@ import java.awt.Point; import java.awt.Window; import java.awt.event.KeyEvent; +import java.awt.EventQueue; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; @@ -39,6 +40,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; +import java.util.Arrays; import javax.accessibility.Accessible; import javax.accessibility.AccessibleAction; @@ -58,6 +60,7 @@ import javax.swing.JMenuItem; import javax.swing.JTextArea; import javax.swing.JList; +import javax.swing.JTree; import javax.swing.KeyStroke; import sun.awt.AWTAccessor; @@ -117,19 +120,14 @@ public void propertyChange(final PropertyChangeEvent evt) { private native void focusChanged(); static T invokeAndWait(final Callable callable, final Component c) { - if (c != null) { - try { - return LWCToolkit.invokeAndWait(callable, c); - } catch (final Exception e) { e.printStackTrace(); } - } - return null; + return invokeAndWait(callable, c, null); } static T invokeAndWait(final Callable callable, final Component c, final T defValue) { T value = null; if (c != null) { try { - value = LWCToolkit.invokeAndWait(callable, c); + value = EventQueue.isDispatchThread() ? callable.call() : LWCToolkit.invokeAndWait(callable, c); } catch (final Exception e) { e.printStackTrace(); } } @@ -725,6 +723,32 @@ public Object[] call() throws Exception { }, c); } + // This method is called from the native + // Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level + private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) { + if (a == null) return null; + return invokeAndWait(new Callable() { + public Object[] call() throws Exception { + ArrayList currentLevelChildren = new ArrayList(); + currentLevelChildren.addAll(Arrays.asList(getChildrenAndRoles(a, c, JAVA_AX_ALL_CHILDREN, allowIgnored))); + ArrayList allChildren = new ArrayList(); + for (int i = 0; i < currentLevelChildren.size(); i += 2) { + if ((((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) || + (((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) || + (whichChildren == JAVA_AX_ALL_CHILDREN)) { + allChildren.add(currentLevelChildren.get(i)); + allChildren.add(currentLevelChildren.get(i + 1)); + allChildren.add(String.valueOf(level)); + } + if (getAccessibleStateSet(((Accessible) currentLevelChildren.get(i)).getAccessibleContext(), c).contains(AccessibleState.EXPANDED)) { + allChildren.addAll(Arrays.asList(getChildrenAndRolesRecursive(((Accessible) currentLevelChildren.get(i)), c, whichChildren, allowIgnored, level + 1))); + } + } + return allChildren.toArray(); + } + }, c); + } + private static final int JAVA_AX_ROWS = 1; private static final int JAVA_AX_COLS = 2; @@ -863,4 +887,18 @@ public Long call() throws Exception { } }, (Component)ax); } + + private static boolean isTreeRootVisible(Accessible a, Component c) { + if (a == null) return false; + + return invokeAndWait(new Callable() { + public Boolean call() throws Exception { + Accessible sa = CAccessible.getSwingAccessible(a); + if (sa instanceof JTree) { + return ((JTree) sa).isRootVisible(); + } + return false; + } + }, c); + } } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 212ddb6969c10..72bfa8d541e47 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -31,11 +31,7 @@ import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; -import javax.swing.JProgressBar; import javax.swing.JTabbedPane; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY; @@ -75,6 +71,8 @@ public static CAccessible getCAccessible(final Accessible a) { private static native void menuOpened(long ptr); private static native void menuClosed(long ptr); private static native void menuItemSelected(long ptr); + private static native void treeNodeExpanded(long ptr); + private static native void treeNodeCollapsed(long ptr); private Accessible accessible; @@ -137,6 +135,13 @@ public void propertyChange(PropertyChangeEvent e) { if (parentAccessible != null) { parentRole = parentAccessible.getAccessibleContext().getAccessibleRole(); } + + if (newValue == AccessibleState.EXPANDED) { + treeNodeExpanded(ptr); + } else if (newValue == AccessibleState.COLLAPSED) { + treeNodeCollapsed(ptr); + } + // At least for now don't handle combo box menu state changes. // This may change when later fixing issues which currently // exist for combo boxes, but for now the following is only diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h index 7b575908bd460..b829335fc2992 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h @@ -48,6 +48,7 @@ BOOL isVertical(JNIEnv *env, jobject axContext, jobject component); BOOL isHorizontal(JNIEnv *env, jobject axContext, jobject component); BOOL isShowing(JNIEnv *env, jobject axContext, jobject component); BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component); +BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component); NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component); jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index 19eb8c2ee8588..ae35dfdb8eb31 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -200,6 +200,20 @@ BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component) return selectable; } +BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component) +{ + GET_ACCESSIBLESTATE_CLASS_RETURN(NO); + DECLARE_STATIC_FIELD_RETURN(jm_EXPANDED, + sjc_AccessibleState, + "EXPANDED", + "Ljavax/accessibility/AccessibleState;", NO ); + jobject axExpandedState = (*env)->GetStaticObjectField(env, sjc_AccessibleState, jm_EXPANDED); + CHECK_EXCEPTION_NULL_RETURN(axExpandedState, NO); + BOOL expanded = containsAxState(env, axContext, axExpandedState, component); + (*env)->DeleteLocalRef(env, axExpandedState); + return expanded; +} + NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component) { GET_CACCESSIBILITY_CLASS_RETURN(NSZeroPoint); @@ -493,7 +507,7 @@ void initializeRoles() [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"]; [sRoles setObject:NSAccessibilityToolbarRole forKey:@"toolbar"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"tooltip"]; - [sRoles setObject:NSAccessibilityBrowserRole forKey:@"tree"]; + [sRoles setObject:NSAccessibilityOutlineRole forKey:@"tree"]; [sRoles setObject:NSAccessibilityUnknownRole forKey:@"unknown"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"]; [sRoles setObject:JavaAccessibilityIgnore forKey:@"window"]; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index b52b0bc4c282d..8e88539c11adf 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -61,6 +61,8 @@ - (void)postSelectedTextChanged; - (void)postSelectionChanged; - (void)postTitleChanged; +- (void)postTreeNodeExpanded; +- (void)postTreeNodeCollapsed; - (BOOL)isEqual:(nonnull id)anObject; - (BOOL)isAccessibleWithEnv:(JNIEnv _Nonnull * _Nonnull)env forAccessible:(nonnull jobject)accessible; @@ -72,6 +74,7 @@ + (CommonComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role andParent:(CommonComponentAccessibility * _Nonnull)parent; + (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored; ++ (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored recursive:(BOOL)recursive; + (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)role index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; @@ -81,6 +84,10 @@ // and we need to create an element whose role corresponds to the role in Java. + (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isWrapped:(BOOL)wrapped; +// The current parameter is used to bypass the check for an item's index on the parent so that the item is created. This is necessary, +// for example, for AccessibleJTreeNode, whose currentComponent has index -1 ++ (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isCurrent:(BOOL)current; + - (jobject _Nullable)axContextWithEnv:(JNIEnv _Nonnull * _Nonnull)env; - (NSView* _Nonnull)view; - (NSWindow* _Nonnull)window; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index e5b842d97755c..c67301cd4509b 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -32,6 +32,7 @@ #import "ThreadUtilities.h" #import "JNIUtilities.h" #import "AWTView.h" +#import "sun_lwawt_macosx_CAccessible.h" // GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; @@ -48,6 +49,12 @@ GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); +static jmethodID jm_getChildrenAndRolesRecursive = NULL; +#define GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRolesRecursive, sjc_CAccessibility, "getChildrenAndRolesRecursive",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZI)[Ljava/lang/Object;", ret); + static jmethodID sjm_getAccessibleComponent = NULL; #define GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(ret) \ GET_CACCESSIBILITY_CLASS_RETURN(ret); \ @@ -113,7 +120,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:44]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:45]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -141,6 +148,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"ComboBoxAccessibility" forKey:@"combobox"]; [rolesMap setObject:@"TabGroupAccessibility" forKey:@"pagetablist"]; [rolesMap setObject:@"ListAccessibility" forKey:@"list"]; + [rolesMap setObject:@"OutlineAccessibility" forKey:@"tree"]; /* * All the components below should be ignored by the accessibility subsystem, @@ -166,9 +174,10 @@ + (void) initializeRolesMap { [rolesMap setObject:IgnoreClassName forKey:@"viewport"]; [rolesMap setObject:IgnoreClassName forKey:@"window"]; - rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:1]; + rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:2]; [rowRolesMapForParent setObject:@"ListRowAccessibility" forKey:@"ListAccessibility"]; + [rowRolesMapForParent setObject:@"OutlineRowAccessibility" forKey:@"OutlineAccessibility"]; /* * Initialize CAccessibility instance @@ -349,6 +358,18 @@ - (void)postMenuItemSelected NSAccessibilityPostNotification(self, (NSString *)kAXMenuItemSelectedNotification); } +- (void)postTreeNodeExpanded +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([[self accessibilitySelectedRows] firstObject], NSAccessibilityRowExpandedNotification); +} + +- (void)postTreeNodeCollapsed +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification([[self accessibilitySelectedRows] firstObject], NSAccessibilityRowCollapsedNotification); +} + - (BOOL)isEqual:(id)anObject { if (![anObject isKindOfClass:[self class]]) return NO; @@ -395,20 +416,34 @@ + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env { } + (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored +{ + return [CommonComponentAccessibility childrenOfParent:parent withEnv:env withChildrenCode:whichChildren allowIgnored:allowIgnored recursive:NO]; +} + ++ (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JNIEnv *)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored recursive:(BOOL)recursive { if (parent->fAccessible == NULL) return nil; - GET_CHILDRENANDROLES_METHOD_RETURN(nil); - jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, - parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); - CHECK_EXCEPTION(); + jobjectArray jchildrenAndRoles = NULL; + if (recursive) { + GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(nil); + jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRolesRecursive, + parent->fAccessible, parent->fComponent, whichChildren, allowIgnored, 0); + CHECK_EXCEPTION(); + } else { + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, whichChildren, allowIgnored); + CHECK_EXCEPTION(); + } if (jchildrenAndRoles == NULL) return nil; jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); - NSMutableArray *children = [NSMutableArray arrayWithCapacity:arrayLen/2]; //childrenAndRoles array contains two elements (child, role) for each child + NSMutableArray *children = [NSMutableArray arrayWithCapacity:(recursive ? arrayLen/3 : arrayLen/2)]; //childrenAndRoles array contains two elements (child, role) for each child NSInteger i; NSUInteger childIndex = (whichChildren >= 0) ? whichChildren : 0; // if we're getting one particular child, make sure to set its index correctly - for(i = 0; i < arrayLen; i+=2) + int inc = recursive ? 3 : 2; + for(i = 0; i < arrayLen; i+=inc) { jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); @@ -425,6 +460,19 @@ + (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JN CommonComponentAccessibility *child = [self createWithParent:parent accessible:jchild role:childJavaRole index:childIndex withEnv:env withView:parent->fView]; + if (recursive && [child respondsToSelector:@selector(accessibleLevel)]) { + jobject jLevel = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+2); + NSString *sLevel = nil; + if (jLevel != NULL) { + sLevel = JavaStringToNSString(env, jLevel); + if (sLevel != nil) { + int level = sLevel.intValue; + [child setAccessibleLevel:level]; + } + (*env)->DeleteLocalRef(env, jLevel); + } + } + (*env)->DeleteLocalRef(env, jchild); (*env)->DeleteLocalRef(env, jchildJavaRole); @@ -437,13 +485,18 @@ + (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JN } + (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view +{ + return [CommonComponentAccessibility createWithAccessible:jaccessible withEnv:env withView:view isCurrent:NO]; +} + ++ (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env withView:(NSView *)view isCurrent:(BOOL)current { GET_ACCESSIBLEINDEXINPARENT_STATIC_METHOD_RETURN(nil); CommonComponentAccessibility *ret = nil; jobject jcomponent = [(AWTView *)view awtComponent:env]; jint index = (*env)->CallStaticIntMethod(env, sjc_CAccessibility, sjm_getAccessibleIndexInParent, jaccessible, jcomponent); CHECK_EXCEPTION(); - if (index >= 0) { + if (index >= 0 || current) { NSString *javaRole = getJavaRole(env, jaccessible, jcomponent); ret = [self createWithAccessible:jaccessible role:javaRole index:index withEnv:env withView:view]; } @@ -455,6 +508,7 @@ + (CommonComponentAccessibility *) createWithAccessible:(jobject)jaccessible rol { return [self createWithParent:nil accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; } + + (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view { return [CommonComponentAccessibility createWithParent:parent accessible:jaccessible role:javaRole index:index withEnv:env withView:view isWrapped:NO]; @@ -574,7 +628,8 @@ - (NSArray *)accessibleChildrenWithChildCode:(NSInteger)childCode NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:childCode - allowIgnored:[[self accessibilityRole] isEqualToString:NSAccessibilityListRole]]; + allowIgnored:([[self accessibilityRole] isEqualToString:NSAccessibilityListRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole]) + recursive:[[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole]]; NSArray *value = nil; if ([children count] > 0) { @@ -1137,3 +1192,35 @@ - (BOOL)accessibilityPerformIncrement { } @end + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: treeNodeExpanded + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded + (JNIEnv *env, jclass jklass, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postTreeNodeExpanded) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: treeNodeCollapsed + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeCollapsed + (JNIEnv *env, jclass jklass, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postTreeNodeCollapsed) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h new file mode 100644 index 0000000000000..48074b209e0be --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListAccessibility.h" + +// This is a tree representation. See: https://developer.apple.com/documentation/appkit/nsoutlineview + +@interface OutlineAccessibility : ListAccessibility + +@property(readonly) BOOL isTreeRootVisible; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m new file mode 100644 index 0000000000000..9c0a7d6c85c83 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "OutlineAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_isTreeRootVisible = NULL; +#define GET_ISTREEROOTVISIBLE_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_isTreeRootVisible, sjc_CAccessibility, "isTreeRootVisible", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", ret); + +@implementation OutlineAccessibility + +- (BOOL)isTreeRootVisible +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_ISTREEROOTVISIBLE_METHOD_RETURN(NO); + return (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isTreeRootVisible, fAccessible, fComponent); +} + +// NSAccessibilityElement protocol methods + +- (NSString *)accessibilityLabel +{ + return [[super accessibilityLabel] isEqualToString:@"list"] ? @"tree" : [super accessibilityLabel]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h new file mode 100644 index 0000000000000..8cc8e1760c607 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ListRowAccessibility.h" + +@interface OutlineRowAccessibility : ListRowAccessibility + +@property(readwrite) int accessibleLevel; + +- (jobject)currentAccessibleWithENV:(JNIEnv *)env; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m new file mode 100644 index 0000000000000..86c644564ad22 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "OutlineRowAccessibility.h" +#import "JavaAccessibilityUtilities.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" +#import "OutlineAccessibility.h" + +static jclass sjc_CAccessible = NULL; +#define GET_CACCESSIBLE_CLASS_RETURN(ret) \ + GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret); + +@implementation OutlineRowAccessibility + +@synthesize accessibleLevel; + +- (jobject)currentAccessibleWithENV:(JNIEnv *)env +{ + jobject jAxContext = getAxContext(env, fAccessible, fComponent); + if (jAxContext == NULL) return NULL; + jclass axContextClass = (*env)->GetObjectClass(env, jAxContext); + DECLARE_METHOD_RETURN(jm_getCurrentComponent, axContextClass, "getCurrentComponent", "()Ljava/awt/Component;", NULL); + jobject newComponent = (*env)->CallObjectMethod(env, jAxContext, jm_getCurrentComponent); + (*env)->DeleteLocalRef(env, jAxContext); + if (newComponent != NULL) { + GET_CACCESSIBLE_CLASS_RETURN(NULL); + DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); + jobject currentAccessible = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, newComponent); + (*env)->DeleteLocalRef(env, newComponent); + return currentAccessible; + } else { + return NULL; + } +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityChildren +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject currentAccessible = [self currentAccessibleWithENV:env]; + if (currentAccessible != NULL) { + CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:currentAccessible withEnv:env withView:self->fView isCurrent:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + if ([children count] != 0) { + return children; + } + } + + return [NSArray arrayWithObject:[CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:self->fJavaRole + index:self->fIndex + withEnv:env + withView:self->fView + isWrapped:YES]]; +} + +- (NSInteger)accessibilityDisclosureLevel +{ + int level = [self accessibleLevel]; + return [(OutlineAccessibility *)[self accessibilityParent] isTreeRootVisible] ? level - 1 : level; +} + +- (BOOL)isAccessibilityDisclosed +{ + return isExpanded([ThreadUtilities getJNIEnv], [self axContextWithEnv:[ThreadUtilities getJNIEnv]], self->fComponent); +} + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + return NSAccessibilityOutlineRowSubrole;; +} + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole;; +} + +- (BOOL)isAccessibilitySelected +{ + return YES; +} + +@end diff --git a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java new file mode 100644 index 0000000000000..d2bd8a6ad0db5 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8267387 + * @summary Create implementation for NSAccessibilityOutline protocol + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTreeTest + */ + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellRenderer; +import java.awt.*; +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleJTreeTest extends AccessibleComponentTest { + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createSampleTree() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + tree.setRootVisible(true); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree sample item test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public void createSampleTreeUnvisableRoot() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree with invisible root.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree sample item invisible root test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public void createSampleTreeNamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTree.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the tab button to move to second tree.\\n\n" + + "If you can hear second tree name: \"second tree\" - tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + JTree secondTree = new JTree(data); + secondTree.getAccessibleContext().setAccessibleName("Second tree"); + tree.setRootVisible(true); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + JScrollPane secondScrollPane = new JScrollPane(secondTree); + panel.add(scrollPane); + panel.add(secondScrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree named test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + + public void createRendererTree() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTree using renderer.\n\n" + + "Turn screen reader on, and Tab to the tree.\n" + + "Press the arrow buttons to move through the tree.\n\n" + + "If you can hear tree components tab further and press PASS, otherwise press FAIL.\n"; + + String root = "Root"; + String[] nodes = new String[] {"One node", "Two node"}; + String[][] leafs = new String[][]{{"leaf 1.1", "leaf 1.2", "leaf 1.3", "leaf 1.4"}, + {"leaf 2.1", "leaf 2.2", "leaf 2.3", "leaf 2.4"}}; + + Hashtable data = new Hashtable(); + for (int i = 0; i < nodes.length; i++) { + data.put(nodes[i], leafs[i]); + } + + JTree tree = new JTree(data); + tree.setRootVisible(true); + tree.setCellRenderer(new AccessibleJTreeTestRenderer()); + + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(tree); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTree renderer item test failed!"; + super.createUI(panel, "AccessibleJTreeTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTreeTest test = new AccessibleJTreeTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTree); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTreeNamed); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createSampleTreeUnvisableRoot); + AccessibleComponentTest.countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createRendererTree); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(AccessibleComponentTest.exceptionString); + } + } + + public static class AccessibleJTreeTestRenderer extends JPanel implements TreeCellRenderer { + private JLabel labelAJT = new JLabel("AJT"); + private JLabel itemName = new JLabel(); + + AccessibleJTreeTestRenderer() { + super(new FlowLayout()); + setFocusable(false); + layoutComponents(); + } + + private void layoutComponents() { + add(labelAJT); + add(itemName); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + itemName.setText((String) (((DefaultMutableTreeNode) value).getUserObject())); + + getAccessibleContext().setAccessibleName(labelAJT.getText() + ", " + itemName.getText()); + return this; + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(Math.min(size.width, 245), size.height); + } + } +} From 280ee5fefe1b7d35620126005f755da7e836c0a5 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Sun, 30 May 2021 15:36:01 +0300 Subject: [PATCH 07/15] 8267388: Create implementation for NSAccessibilityTable protocol --- .../awt/JavaAccessibilityUtilities.m | 2 +- .../awt/JavaComponentAccessibility.m | 6 +- .../libawt_lwawt/awt/a11y/CellAccessibility.h | 28 ++ .../libawt_lwawt/awt/a11y/CellAccessibility.m | 65 +++++ .../awt/a11y/ColumnAccessibility.h | 31 +++ .../awt/a11y/ColumnAccessibility.m | 111 ++++++++ .../awt/a11y/CommonComponentAccessibility.m | 3 +- .../awt/a11y/TableAccessibility.h | 37 +++ .../awt/a11y/TableAccessibility.m | 245 ++++++++++++++++++ .../awt/a11y/TableRowAccessibility.h | 31 +++ .../awt/a11y/TableRowAccessibility.m | 155 +++++++++++ .../java/awt/a11y/AccessibleJTableTest.java | 109 ++++++++ 12 files changed, 818 insertions(+), 5 deletions(-) create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h create mode 100644 src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m create mode 100644 test/jdk/java/awt/a11y/AccessibleJTableTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index ae35dfdb8eb31..9520c3d552b49 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -501,7 +501,7 @@ void initializeRoles() [sRoles setObject:NSAccessibilitySplitGroupRole forKey:@"splitpane"]; [sRoles setObject:NSAccessibilityValueIndicatorRole forKey:@"statusbar"]; [sRoles setObject:NSAccessibilityGroupRole forKey:@"swingcomponent"]; - [sRoles setObject:NSAccessibilityGridRole forKey:@"table"]; + [sRoles setObject:NSAccessibilityTableRole forKey:@"table"]; [sRoles setObject:NSAccessibilityTextFieldRole forKey:@"text"]; [sRoles setObject:NSAccessibilityTextAreaRole forKey:@"textarea"]; // supports top/bottom of document notifications: CAccessability.getAccessibleRole() [sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"]; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m index a2181ec1c31a0..9db8342f8444a 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m @@ -117,7 +117,7 @@ - (void)getActionsWithEnv:(JNIEnv *)env; - (id)accessibilityValueAttribute; @end -@interface TableAccessibility : JavaComponentAccessibility { +@interface TableLegacyAccessibility : JavaComponentAccessibility { } - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env; @@ -370,7 +370,7 @@ + (JavaComponentAccessibility *) createWithParent:(JavaComponentAccessibility *) if ([javaRole isEqualToString:@"pagetablist"]) { newChild = [TabGroupLegacyAccessibility alloc]; } else if ([javaRole isEqualToString:@"table"]) { - newChild = [TableAccessibility alloc]; + newChild = [TableLegacyAccessibility alloc]; } else { NSString *nsRole = [sRoles objectForKey:javaRole]; if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] || @@ -1846,7 +1846,7 @@ - (jobject)tabGroup #define JAVA_AX_ROWS (1) #define JAVA_AX_COLS (2) -@implementation TableAccessibility +@implementation TableLegacyAccessibility - (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h new file mode 100644 index 0000000000000..bcaa0269576ac --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface CellAccessibility : CommonComponentAccessibility +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m new file mode 100644 index 0000000000000..0ebb28861de5d --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CellAccessibility.h" +#import "ThreadUtilities.h" + +@implementation CellAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityCellRole;; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + NSString *javaRole = [self javaRole]; + CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self + accessible:self->fAccessible + role:javaRole + index:self->fIndex + withEnv:[ThreadUtilities getJNIEnv] + withView:self->fView + isWrapped:YES]; + return [NSArray arrayWithObject:newChild]; + } else { + return children; + } +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h new file mode 100644 index 0000000000000..a78afaa924b01 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface ColumnAccessibility : CommonComponentAccessibility + +@property(readonly) NSUInteger columnNumberInTable; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m new file mode 100644 index 0000000000000..6a0ad3f129b3d --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "CellAccessibility.h" +#import "ColumnAccessibility.h" +#import "TableAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation ColumnAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityColumnRole; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == NULL) { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + CommonComponentAccessibility *parent = [self accessibilityParent]; + if (parent->fAccessible == NULL) return nil; + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, JAVA_AX_ALL_CHILDREN, NO); + CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; + + NSUInteger childIndex = [self columnNumberInTable]; + + int inc = [(TableAccessibility *)[self accessibilityParent] accessibleColCount] * 2; + NSInteger i = childIndex * 2; + for(i; i < arrayLen; i += inc) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self + withEnv:env + withAccessible:jchild + withIndex:childIndex + withView:self->fView + withJavaRole:childJavaRole]; + [childrenCells addObject:[child autorelease]]; + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + childIndex += (inc / 2); + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + return childrenCells; + } else { + return children; + } +} + +- (NSUInteger)columnNumberInTable +{ + return self->fIndex; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index c67301cd4509b..f798adbe74574 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -120,7 +120,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:45]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:46]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -149,6 +149,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"TabGroupAccessibility" forKey:@"pagetablist"]; [rolesMap setObject:@"ListAccessibility" forKey:@"list"]; [rolesMap setObject:@"OutlineAccessibility" forKey:@"tree"]; + [rolesMap setObject:@"TableAccessibility" forKey:@"table"]; /* * All the components below should be ignored by the accessibility subsystem, diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h new file mode 100644 index 0000000000000..2b1ad908238ae --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TableAccessibility : CommonComponentAccessibility + +@property(readonly) int accessibleRowCount; +@property(readonly) int accessibleColCount; +@property(readonly) NSArray *selectedAccessibleRows; +@property(readonly) NSArray *selectedAccessibleColumns; +- (BOOL)isAccessibleChildSelectedFromIndex:(int)index; +- (int) accessibleRowAtIndex:(int)index; +- (int) accessibleColumnAtIndex:(int)index; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m new file mode 100644 index 0000000000000..793b50740ce9d --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#import "TableRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "TableAccessibility.h" +#import "CellAccessibility.h" +#import "ColumnAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +@implementation TableAccessibility + +- (int)accessibleRowCount +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleRowCount, clsInfo, "getAccessibleRowCount", "()I", 0); + jint javaRowsCount = (*env)->CallIntMethod(env, axContext, jm_getAccessibleRowCount); + (*env)->DeleteLocalRef(env, axContext); + return (int)javaRowsCount; +} + +- (int)accessibleColCount +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleColumnCount, clsInfo, "getAccessibleColumnCount", "()I", 0); + jint javaColsCount = (*env)->CallIntMethod(env, axContext, jm_getAccessibleColumnCount); + (*env)->DeleteLocalRef(env, axContext); + return (int)javaColsCount; +} + +- (NSArray *)selectedAccessibleRows +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return nil; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getSelectedAccessibleRows, clsInfo, "getSelectedAccessibleRows", "()[I", nil); + jintArray selectedRowNumbers = (*env)->CallObjectMethod(env, axContext, jm_getSelectedAccessibleRows); + (*env)->DeleteLocalRef(env, axContext); + if (selectedRowNumbers == NULL) { + return nil; + } + jsize arrayLen = (*env)->GetArrayLength(env, selectedRowNumbers); + jint *indexsis = (*env)->GetIntArrayElements(env, selectedRowNumbers, 0); + NSMutableArray *nsArraySelectedRowNumbers = [NSMutableArray arrayWithCapacity:arrayLen]; + for (int i = 0; i < arrayLen; i++) { + [nsArraySelectedRowNumbers addObject:[NSNumber numberWithInt:indexsis[i]]]; + } + (*env)->DeleteLocalRef(env, selectedRowNumbers); + return [NSArray arrayWithArray:nsArraySelectedRowNumbers]; +} + +- (NSArray *)selectedAccessibleColumns +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return nil; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getSelectedAccessibleColumns, clsInfo, "getSelectedAccessibleColumns", "()[I", nil); + jintArray selectedColumnNumbers = (*env)->CallObjectMethod(env, axContext, jm_getSelectedAccessibleColumns); + (*env)->DeleteLocalRef(env, axContext); + if (selectedColumnNumbers == NULL) { + return nil; + } + jsize arrayLen = (*env)->GetArrayLength(env, selectedColumnNumbers); + jint *indexsis = (*env)->GetIntArrayElements(env, selectedColumnNumbers, 0); + NSMutableArray *nsArraySelectedColumnNumbers = [NSMutableArray arrayWithCapacity:arrayLen]; + for (int i = 0; i < arrayLen; i++) { + [nsArraySelectedColumnNumbers addObject:[NSNumber numberWithInt:indexsis[i]]]; + } + (*env)->DeleteLocalRef(env, selectedColumnNumbers); + return [NSArray arrayWithArray:nsArraySelectedColumnNumbers]; +} + +- (int)accessibleRowAtIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleRowAtIndex, clsInfo, "getAccessibleRowAtIndex", "(I)I", -1); + jint rowAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleRowAtIndex, (jint)index); + (*env)->DeleteLocalRef(env, axContext); + return (int)rowAtIndex; +} + +- (int)accessibleColumnAtIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return 0; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_getAccessibleColumnAtIndex, clsInfo, "getAccessibleColumnAtIndex", "(I)I", -1); + jint columnAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleColumnAtIndex, (jint)index); + (*env)->DeleteLocalRef(env, axContext); + return (int)columnAtIndex; +} + +- (BOOL) isAccessibleChildSelectedFromIndex:(int)index +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + jobject axContext = [self axContextWithEnv:env]; + if (axContext == NULL) return NO; + jclass clsInfo = (*env)->GetObjectClass(env, axContext); + DECLARE_METHOD_RETURN(jm_isAccessibleChildSelected, clsInfo, "isAccessibleChildSelected", "(I)Z", NO); + jboolean isAccessibleChildSelected = (*env)->CallIntMethod(env, axContext, jm_isAccessibleChildSelected, (jint)index); + (*env)->DeleteLocalRef(env, axContext); + return isAccessibleChildSelected; +} + +// NSAccessibilityElement protocol methods + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [self accessibilityRows]; + NSArray *columns = [self accessibilityColumns]; + NSMutableArray *results = [NSMutableArray arrayWithCapacity:[children count] + [columns count]]; + [results addObjectsFromArray:children]; + [results addObjectsFromArray:columns]; + return [NSArray arrayWithArray:results]; +} + +- (NSArray *)accessibilitySelectedChildren +{ + return [self accessibilitySelectedRows]; +} + +- (NSArray *)accessibilityRows +{ + int rowCount = [self accessibleRowCount]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:rowCount]; + for (int i = 0; i < rowCount; i++) { + [children addObject:[[TableRowAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i + withView:[self view] + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:children]; +} + +- (nullable NSArray> *)accessibilitySelectedRows +{ + NSArray *selectedRowIndexses = [self selectedAccessibleRows]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:[selectedRowIndexses count]]; + for (NSNumber *index in selectedRowIndexses) { + [children addObject:[[TableRowAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:index.unsignedIntValue + withView:[self view] + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:children]; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel] == NULL ? @"table" : [super accessibilityLabel]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (nullable NSArray *)accessibilityColumns +{ + int colCount = [self accessibleColCount]; + NSMutableArray *columns = [NSMutableArray arrayWithCapacity:colCount]; + for (int i = 0; i < colCount; i++) { + [columns addObject:[[ColumnAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i + withView:self->fView + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:columns]; +} + +- (nullable NSArray *)accessibilitySelectedColumns +{ + NSArray *indexes = [self selectedAccessibleColumns]; + NSMutableArray *columns = [NSMutableArray arrayWithCapacity:[indexes count]]; + for (NSNumber *i in indexes) { + [columns addObject:[[ColumnAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:i.unsignedIntValue + withView:self->fView + withJavaRole:JavaAccessibilityIgnore]]; + } + return [NSArray arrayWithArray:columns]; +} + +/* Other optional NSAccessibilityTable Methods +- (nullable NSArray> *)accessibilityVisibleRows; +- (nullable NSArray *)accessibilityColumns; +- (nullable NSArray *)accessibilitySelectedColumns; +- (nullable NSArray *)accessibilityVisibleColumns; + + - (nullable NSArray *)accessibilitySelectedCells; +- (nullable NSArray *)accessibilityVisibleCells; +- (nullable NSArray *)accessibilityRowHeaderUIElements; +- (nullable NSArray *)accessibilityColumnHeaderUIElements; + */ + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h new file mode 100644 index 0000000000000..afa9eb44e3f22 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "CommonComponentAccessibility.h" + +@interface TableRowAccessibility : CommonComponentAccessibility + +@property(readonly) NSUInteger rowNumberInTable; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m new file mode 100644 index 0000000000000..ef91fce8c702d --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "TableRowAccessibility.h" +#import "JavaAccessibilityAction.h" +#import "JavaAccessibilityUtilities.h" +#import "TableAccessibility.h" +#import "CellAccessibility.h" +#import "ThreadUtilities.h" +#import "JNIUtilities.h" + +static jclass sjc_CAccessibility = NULL; + +static jmethodID jm_getChildrenAndRoles = NULL; +#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); + +@implementation TableRowAccessibility + +// NSAccessibilityElement protocol methods + +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityRowRole; +} + +- (NSAccessibilitySubrole)accessibilitySubrole +{ + return NSAccessibilityTableRowSubrole; +} + +- (BOOL)isAccessibilityEnabled +{ + return YES; +} + +- (NSArray *)accessibilityChildren +{ + NSArray *children = [super accessibilityChildren]; + if (children == nil) { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + CommonComponentAccessibility *parent = [self accessibilityParent]; + if (parent->fAccessible == NULL) return nil; + GET_CHILDRENANDROLES_METHOD_RETURN(nil); + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, + parent->fAccessible, parent->fComponent, JAVA_AX_ALL_CHILDREN, NO); + CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; + + jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); + NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; + + NSUInteger childIndex = [self rowNumberInTable] * [(TableAccessibility *)parent accessibleColCount]; + NSInteger i = childIndex * 2; + NSInteger n = ([self rowNumberInTable] + 1) * [(TableAccessibility *)parent accessibleColCount] * 2; + for(i; i < n; i+=2) + { + jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); + jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); + + NSString *childJavaRole = nil; + if (jchildJavaRole != NULL) { + DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); + DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); + jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); + CHECK_EXCEPTION(); + childJavaRole = JavaStringToNSString(env, jkey); + (*env)->DeleteLocalRef(env, jkey); + } + + CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self + withEnv:env + withAccessible:jchild + withIndex:childIndex + withView:self->fView + withJavaRole:childJavaRole]; + [childrenCells addObject:[child autorelease]]; + + (*env)->DeleteLocalRef(env, jchild); + (*env)->DeleteLocalRef(env, jchildJavaRole); + + childIndex++; + } + (*env)->DeleteLocalRef(env, jchildrenAndRoles); + return childrenCells; + } else { + return children; + } +} + +- (NSInteger)accessibilityIndex +{ + return [[self accessibilityParent] accessibilityIndexOfChild:self]; +} + +- (NSString *)accessibilityLabel +{ + NSString *accessibilityName = @""; + NSArray *children = [self accessibilityChildren]; + for (id cell in children) { + if ([accessibilityName isEqualToString:@""]) { + accessibilityName = [cell accessibilityLabel]; + } else { + accessibilityName = [accessibilityName stringByAppendingFormat:@", %@", [cell accessibilityLabel]]; + } + } + return accessibilityName; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSUInteger)rowNumberInTable +{ + return (NSUInteger)self->fIndex; +} + +- (NSRect)accessibilityFrame +{ + int height = [[[self accessibilityChildren] objectAtIndex:0] accessibilityFrame].size.height; + int width = 0; + NSPoint point = [[[self accessibilityChildren] objectAtIndex:0] accessibilityFrame].origin; + for (id cell in [self accessibilityChildren]) { + width += [cell accessibilityFrame].size.width; + } + return NSMakeRect(point.x, point.y, width, height); +} + +@end + diff --git a/test/jdk/java/awt/a11y/AccessibleJTableTest.java b/test/jdk/java/awt/a11y/AccessibleJTableTest.java new file mode 100644 index 0000000000000..ed52be7fdfb41 --- /dev/null +++ b/test/jdk/java/awt/a11y/AccessibleJTableTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8267388 + * @summary Create implementation for NSAccessibilityTable protocol + * @author Artem.Semenov@jetbrains.com + * @run main/manual AccessibleJTableTest + */ + +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AccessibleJTableTest extends AccessibleComponentTest { + private static final String[] columnNames = {"One", "Two", "Three"}; + private static final String[][] data = { + {"One1", "Two1", "Three1"}, + {"One2", "Two2", "Three2"}, + {"One3", "Two3", "Three3"}, + {"One4", "Two4", "Three4"}, + {"One5", "Two5", "Three5"} + }; + + @Override + public CountDownLatch createCountDownLatch() { + return new CountDownLatch(1); + } + + public void createUI() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of JTable.\n\n" + + "Turn screen reader on, and Tab to the table.\n" + + "On Windows press the arrow buttons to move through the table.\n\n" + + "On MacOS, use the up and down arrow buttons to move through rows, and VoiceOver fast navigation to move through columns.\n\n" + + "If you can hear table cells ctrl+tab further and press PASS, otherwise press FAIL.\n"; + + JTable table = new JTable(data, columnNames); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(table); + panel.add(scrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTable test failed!"; + super.createUI(panel, "AccessibleJTableTest"); + } + + public void createUINamed() { + INSTRUCTIONS = "INSTRUCTIONS:\n" + + "Check a11y of named JTable.\n\n" + + "Turn screen reader on, and Tab to the table.\n" + + "Press the ctrl+tab button to move to second table.\n\n" + + "If you can hear second table name: \"second table\" - ctrl+tab further and press PASS, otherwise press FAIL.\n"; + + JTable table = new JTable(data, columnNames); + JTable secondTable = new JTable(data, columnNames); + secondTable.getAccessibleContext().setAccessibleName("Second table"); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + JScrollPane scrollPane = new JScrollPane(table); + JScrollPane secondScrollPane = new JScrollPane(secondTable); + panel.add(scrollPane); + panel.add(secondScrollPane); + panel.setFocusable(false); + exceptionString = "AccessibleJTable test failed!"; + super.createUI(panel, "AccessibleJTableTest"); + } + + public static void main(String[] args) throws Exception { + AccessibleJTableTest test = new AccessibleJTableTest(); + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createUI); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + + countDownLatch = test.createCountDownLatch(); + SwingUtilities.invokeAndWait(test::createUINamed); + countDownLatch.await(15, TimeUnit.MINUTES); + if (!testResult) { + throw new RuntimeException(exceptionString); + } + } +} From e9b966b515835aade29fb37bfd2e128ecc9cffc3 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Fri, 2 Jul 2021 15:38:10 +0300 Subject: [PATCH 08/15] Even on Linux? I meant if the test will be run on the platform where a11y is not implemented then this will be --- test/jdk/java/awt/a11y/AccessibleJComboboxTest.java | 1 + test/jdk/java/awt/a11y/AccessibleJListTest.java | 1 + test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java | 1 + test/jdk/java/awt/a11y/AccessibleJTreeTest.java | 1 + test/jdk/java/awt/a11y/AccessibleTextTest.java | 1 + 5 files changed, 5 insertions(+) diff --git a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java index f74aaaba3beee..2879eac00ede3 100644 --- a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java @@ -28,6 +28,7 @@ * @summary Create implementation for NSAccessibilityComboBox protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJComboboxTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; diff --git a/test/jdk/java/awt/a11y/AccessibleJListTest.java b/test/jdk/java/awt/a11y/AccessibleJListTest.java index c6f093c755576..dac5e1524cd3d 100644 --- a/test/jdk/java/awt/a11y/AccessibleJListTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJListTest.java @@ -28,6 +28,7 @@ * @summary Create implementation for NSAccessibilityList protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJListTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; diff --git a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java index 70d9ffd5b339e..cbf4bfe89bb4a 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java @@ -28,6 +28,7 @@ * @summary Create implementation for NSAccessibilityTabGroup protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJTabbedPaneTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; diff --git a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java index d2bd8a6ad0db5..d6d415d072f0a 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java @@ -28,6 +28,7 @@ * @summary Create implementation for NSAccessibilityOutline protocol * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJTreeTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; diff --git a/test/jdk/java/awt/a11y/AccessibleTextTest.java b/test/jdk/java/awt/a11y/AccessibleTextTest.java index f2e96c2b02920..f70a1625d6292 100644 --- a/test/jdk/java/awt/a11y/AccessibleTextTest.java +++ b/test/jdk/java/awt/a11y/AccessibleTextTest.java @@ -28,6 +28,7 @@ * @summary Create implementation for NSAccessibilityNavigableStaticText protocol * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleTextTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; From ba09569611c840d8156b53c247c0f9bccda81187 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Tue, 13 Jul 2021 17:47:11 +0300 Subject: [PATCH 09/15] 1. Fixed memory management problems, due to which a11y cells in tables, as well as a11y components nested in list rows, disappeared; 2. Added checks for exceptions; 3. corrected descriptions of tests; 4. Fixed a problem with the pronunciation of the edited text. 5. Fixed a problem due to which the line number in the table was not pronounced correctly. --- .../sun/lwawt/macosx/CAccessibility.java | 17 ++ .../macosx/native/libawt_lwawt/awt/AWTView.m | 2 +- .../awt/a11y/ColumnAccessibility.m | 4 +- .../awt/a11y/ComboBoxAccessibility.m | 2 + .../awt/a11y/CommonComponentAccessibility.m | 2 +- .../awt/a11y/ListRowAccessibility.m | 2 +- .../awt/a11y/OutlineAccessibility.m | 4 +- .../awt/a11y/OutlineRowAccessibility.m | 2 + .../awt/a11y/TableAccessibility.h | 4 - .../awt/a11y/TableAccessibility.m | 146 +++++++++--------- .../awt/a11y/TableRowAccessibility.m | 13 +- .../awt/a11y/AccessibleJComboboxTest.java | 2 +- .../java/awt/a11y/AccessibleJListTest.java | 2 +- .../awt/a11y/AccessibleJTabbedPaneTest.java | 2 +- .../java/awt/a11y/AccessibleJTableTest.java | 3 +- .../java/awt/a11y/AccessibleJTreeTest.java | 2 +- .../jdk/java/awt/a11y/AccessibleTextTest.java | 2 +- 17 files changed, 110 insertions(+), 101 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index aae83e76fc735..77d027ec98371 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -769,6 +769,23 @@ public static int getTableInfo(final Accessible a, final Component c, }, c); } + private static int[] getTableSelectedInfo(final Accessible a, final Component c, + final int info) { + if (a == null) return null; + return invokeAndWait(() -> { + AccessibleContext ac = a.getAccessibleContext(); + AccessibleTable table = ac.getAccessibleTable(); + if (table != null) { + if (info == JAVA_AX_COLS) { + return table.getSelectedAccessibleColumns(); + } else if (info == JAVA_AX_ROWS) { + return table.getSelectedAccessibleRows(); + } + } + return null; + }, c); + } + private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) { String text = l.getText(); if (text != null && text.length() > 0) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index 130a2611f0a14..8e44052333c2c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -676,7 +676,7 @@ - (NSString *)accessibilitySelectedText - (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText { id focused = [self accessibilityFocusedUIElement]; - if ([focused respondsToSelector:@selector(setAccessibilitySelectedText)]) { + if ([focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) { [focused setAccessibilitySelectedText:accessibilitySelectedText]; } } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m index 6a0ad3f129b3d..2269128061f61 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -66,7 +66,7 @@ - (NSArray *)accessibilityChildren NSUInteger childIndex = [self columnNumberInTable]; - int inc = [(TableAccessibility *)[self accessibilityParent] accessibleColCount] * 2; + int inc = [(TableAccessibility *)[self accessibilityParent] accessibilityRowCount] * 2; NSInteger i = childIndex * 2; for(i; i < arrayLen; i += inc) { @@ -89,7 +89,7 @@ - (NSArray *)accessibilityChildren withIndex:childIndex withView:self->fView withJavaRole:childJavaRole]; - [childrenCells addObject:[child autorelease]]; + [childrenCells addObject:[[child retain] autorelease]]; (*env)->DeleteLocalRef(env, jchild); (*env)->DeleteLocalRef(env, jchildJavaRole); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m index 4f9b65d1dcf70..63c00414d66fc 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m @@ -46,6 +46,7 @@ - (id)accessibilityValue { jclass axContextClass = (*env)->GetObjectClass(env, axContext); DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axContextClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil); jobject axSelectedChild = (*env)->CallObjectMethod(env, axContext, jm_getAccessibleSelection, 0); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, axContext); if (axSelectedChild == NULL) { return nil; @@ -53,6 +54,7 @@ - (id)accessibilityValue { GET_CACCESSIBILITY_CLASS_RETURN(nil); GET_ACCESSIBLENAME_METHOD_RETURN(nil); jobject childName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, axSelectedChild, fComponent); + CHECK_EXCEPTION(); if (childName == NULL) { (*env)->DeleteLocalRef(env, axSelectedChild); return nil; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index f798adbe74574..45cd637747ca1 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -629,7 +629,7 @@ - (NSArray *)accessibleChildrenWithChildCode:(NSInteger)childCode NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:childCode - allowIgnored:([[self accessibilityRole] isEqualToString:NSAccessibilityListRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole]) + allowIgnored:([[self accessibilityRole] isEqualToString:NSAccessibilityListRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole] || [[self accessibilityRole] isEqualToString:NSAccessibilityTableRole]) recursive:[[self accessibilityRole] isEqualToString:NSAccessibilityOutlineRole]]; NSArray *value = nil; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m index 6dbb0b95cba4e..9212b181aeb50 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m @@ -49,7 +49,7 @@ - (NSArray *)accessibilityChildren withEnv:[ThreadUtilities getJNIEnv] withView:self->fView isWrapped:YES]; - return [NSArray arrayWithObject:[newChild autorelease]]; + return [NSArray arrayWithObject:newChild]; } else { return children; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m index 9c0a7d6c85c83..acc5727cd6b31 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m @@ -41,7 +41,9 @@ - (BOOL)isTreeRootVisible { JNIEnv *env = [ThreadUtilities getJNIEnv]; GET_ISTREEROOTVISIBLE_METHOD_RETURN(NO); - return (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isTreeRootVisible, fAccessible, fComponent); + bool isTreeRootVisible = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isTreeRootVisible, fAccessible, fComponent); + CHECK_EXCEPTION(); + return isTreeRootVisible; } // NSAccessibilityElement protocol methods diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m index 86c644564ad22..f1d8649f2e600 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m @@ -44,11 +44,13 @@ - (jobject)currentAccessibleWithENV:(JNIEnv *)env jclass axContextClass = (*env)->GetObjectClass(env, jAxContext); DECLARE_METHOD_RETURN(jm_getCurrentComponent, axContextClass, "getCurrentComponent", "()Ljava/awt/Component;", NULL); jobject newComponent = (*env)->CallObjectMethod(env, jAxContext, jm_getCurrentComponent); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, jAxContext); if (newComponent != NULL) { GET_CACCESSIBLE_CLASS_RETURN(NULL); DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); jobject currentAccessible = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, newComponent); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, newComponent); return currentAccessible; } else { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h index 2b1ad908238ae..4f746706f5c53 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h @@ -26,10 +26,6 @@ @interface TableAccessibility : CommonComponentAccessibility -@property(readonly) int accessibleRowCount; -@property(readonly) int accessibleColCount; -@property(readonly) NSArray *selectedAccessibleRows; -@property(readonly) NSArray *selectedAccessibleColumns; - (BOOL)isAccessibleChildSelectedFromIndex:(int)index; - (int) accessibleRowAtIndex:(int)index; - (int) accessibleColumnAtIndex:(int)index; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m index 793b50740ce9d..d38a3faeca745 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -31,75 +31,58 @@ #import "ColumnAccessibility.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "CellAccessibility.h" + +#define JAVA_AX_ROWS (1) +#define JAVA_AX_COLS (2) + +static jclass sjc_CAccessibility = NULL; + +static jmethodID sjm_getAccessibleName = NULL; +#define GET_ACCESSIBLENAME_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); @implementation TableAccessibility -- (int)accessibleRowCount +- (id)getTableInfo:(jint)info { - JNIEnv *env = [ThreadUtilities getJNIEnv]; - jobject axContext = [self axContextWithEnv:env]; - if (axContext == NULL) return 0; - jclass clsInfo = (*env)->GetObjectClass(env, axContext); - DECLARE_METHOD_RETURN(jm_getAccessibleRowCount, clsInfo, "getAccessibleRowCount", "()I", 0); - jint javaRowsCount = (*env)->CallIntMethod(env, axContext, jm_getAccessibleRowCount); - (*env)->DeleteLocalRef(env, axContext); - return (int)javaRowsCount; -} + if (fAccessible == NULL) return 0; -- (int)accessibleColCount -{ - JNIEnv *env = [ThreadUtilities getJNIEnv]; - jobject axContext = [self axContextWithEnv:env]; - if (axContext == NULL) return 0; - jclass clsInfo = (*env)->GetObjectClass(env, axContext); - DECLARE_METHOD_RETURN(jm_getAccessibleColumnCount, clsInfo, "getAccessibleColumnCount", "()I", 0); - jint javaColsCount = (*env)->CallIntMethod(env, axContext, jm_getAccessibleColumnCount); - (*env)->DeleteLocalRef(env, axContext); - return (int)javaColsCount; + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getTableInfo, sjc_CAccessibility, "getTableInfo", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)I", nil); + jint count = (*env)->CallStaticIntMethod(env, sjc_CAccessibility, jm_getTableInfo, fAccessible, + fComponent, info); + CHECK_EXCEPTION(); + NSNumber *index = [NSNumber numberWithInt:count]; + return index; } -- (NSArray *)selectedAccessibleRows +- (NSArray *)getTableSelectedInfo:(jint)info { - JNIEnv *env = [ThreadUtilities getJNIEnv]; - jobject axContext = [self axContextWithEnv:env]; - if (axContext == NULL) return nil; - jclass clsInfo = (*env)->GetObjectClass(env, axContext); - DECLARE_METHOD_RETURN(jm_getSelectedAccessibleRows, clsInfo, "getSelectedAccessibleRows", "()[I", nil); - jintArray selectedRowNumbers = (*env)->CallObjectMethod(env, axContext, jm_getSelectedAccessibleRows); - (*env)->DeleteLocalRef(env, axContext); - if (selectedRowNumbers == NULL) { - return nil; - } - jsize arrayLen = (*env)->GetArrayLength(env, selectedRowNumbers); - jint *indexsis = (*env)->GetIntArrayElements(env, selectedRowNumbers, 0); - NSMutableArray *nsArraySelectedRowNumbers = [NSMutableArray arrayWithCapacity:arrayLen]; - for (int i = 0; i < arrayLen; i++) { - [nsArraySelectedRowNumbers addObject:[NSNumber numberWithInt:indexsis[i]]]; - } - (*env)->DeleteLocalRef(env, selectedRowNumbers); - return [NSArray arrayWithArray:nsArraySelectedRowNumbers]; -} + if (fAccessible == NULL) return 0; -- (NSArray *)selectedAccessibleColumns -{ - JNIEnv *env = [ThreadUtilities getJNIEnv]; - jobject axContext = [self axContextWithEnv:env]; - if (axContext == NULL) return nil; - jclass clsInfo = (*env)->GetObjectClass(env, axContext); - DECLARE_METHOD_RETURN(jm_getSelectedAccessibleColumns, clsInfo, "getSelectedAccessibleColumns", "()[I", nil); - jintArray selectedColumnNumbers = (*env)->CallObjectMethod(env, axContext, jm_getSelectedAccessibleColumns); - (*env)->DeleteLocalRef(env, axContext); - if (selectedColumnNumbers == NULL) { + JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getTableSelectedInfo, sjc_CAccessibility, "getTableSelectedInfo", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)[I", nil); + jintArray selected = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getTableSelectedInfo, fAccessible, + fComponent, info); + CHECK_EXCEPTION(); + if (selected == NULL) { return nil; } - jsize arrayLen = (*env)->GetArrayLength(env, selectedColumnNumbers); - jint *indexsis = (*env)->GetIntArrayElements(env, selectedColumnNumbers, 0); - NSMutableArray *nsArraySelectedColumnNumbers = [NSMutableArray arrayWithCapacity:arrayLen]; + jsize arrayLen = (*env)->GetArrayLength(env, selected); + jint *indexsis = (*env)->GetIntArrayElements(env, selected, 0); + NSMutableArray *nsArraySelected = [NSMutableArray arrayWithCapacity:arrayLen]; for (int i = 0; i < arrayLen; i++) { - [nsArraySelectedColumnNumbers addObject:[NSNumber numberWithInt:indexsis[i]]]; + [nsArraySelected addObject:[NSNumber numberWithInt:indexsis[i]]]; } - (*env)->DeleteLocalRef(env, selectedColumnNumbers); - return [NSArray arrayWithArray:nsArraySelectedColumnNumbers]; + (*env)->DeleteLocalRef(env, selected); + return [NSArray arrayWithArray:nsArraySelected]; } - (int)accessibleRowAtIndex:(int)index @@ -110,6 +93,7 @@ - (int)accessibleRowAtIndex:(int)index jclass clsInfo = (*env)->GetObjectClass(env, axContext); DECLARE_METHOD_RETURN(jm_getAccessibleRowAtIndex, clsInfo, "getAccessibleRowAtIndex", "(I)I", -1); jint rowAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleRowAtIndex, (jint)index); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, axContext); return (int)rowAtIndex; } @@ -122,6 +106,7 @@ - (int)accessibleColumnAtIndex:(int)index jclass clsInfo = (*env)->GetObjectClass(env, axContext); DECLARE_METHOD_RETURN(jm_getAccessibleColumnAtIndex, clsInfo, "getAccessibleColumnAtIndex", "(I)I", -1); jint columnAtIndex = (*env)->CallIntMethod(env, axContext, jm_getAccessibleColumnAtIndex, (jint)index); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, axContext); return (int)columnAtIndex; } @@ -134,6 +119,7 @@ - (BOOL) isAccessibleChildSelectedFromIndex:(int)index jclass clsInfo = (*env)->GetObjectClass(env, axContext); DECLARE_METHOD_RETURN(jm_isAccessibleChildSelected, clsInfo, "isAccessibleChildSelected", "(I)Z", NO); jboolean isAccessibleChildSelected = (*env)->CallIntMethod(env, axContext, jm_isAccessibleChildSelected, (jint)index); + CHECK_EXCEPTION(); (*env)->DeleteLocalRef(env, axContext); return isAccessibleChildSelected; } @@ -142,12 +128,7 @@ - (BOOL) isAccessibleChildSelectedFromIndex:(int)index - (NSArray *)accessibilityChildren { - NSArray *children = [self accessibilityRows]; - NSArray *columns = [self accessibilityColumns]; - NSMutableArray *results = [NSMutableArray arrayWithCapacity:[children count] + [columns count]]; - [results addObjectsFromArray:children]; - [results addObjectsFromArray:columns]; - return [NSArray arrayWithArray:results]; + return [self accessibilityRows]; } - (NSArray *)accessibilitySelectedChildren @@ -157,7 +138,7 @@ - (NSArray *)accessibilitySelectedChildren - (NSArray *)accessibilityRows { - int rowCount = [self accessibleRowCount]; + int rowCount = [self accessibilityRowCount]; NSMutableArray *children = [NSMutableArray arrayWithCapacity:rowCount]; for (int i = 0; i < rowCount; i++) { [children addObject:[[TableRowAccessibility alloc] initWithParent:self @@ -172,7 +153,7 @@ - (NSArray *)accessibilityRows - (nullable NSArray> *)accessibilitySelectedRows { - NSArray *selectedRowIndexses = [self selectedAccessibleRows]; + NSArray *selectedRowIndexses = [self getTableSelectedInfo:JAVA_AX_ROWS]; NSMutableArray *children = [NSMutableArray arrayWithCapacity:[selectedRowIndexses count]]; for (NSNumber *index in selectedRowIndexses) { [children addObject:[[TableRowAccessibility alloc] initWithParent:self @@ -202,7 +183,7 @@ - (id)accessibilityParent - (nullable NSArray *)accessibilityColumns { - int colCount = [self accessibleColCount]; + int colCount = [self accessibilityColumnCount]; NSMutableArray *columns = [NSMutableArray arrayWithCapacity:colCount]; for (int i = 0; i < colCount; i++) { [columns addObject:[[ColumnAccessibility alloc] initWithParent:self @@ -217,7 +198,7 @@ - (nullable NSArray *)accessibilityColumns - (nullable NSArray *)accessibilitySelectedColumns { - NSArray *indexes = [self selectedAccessibleColumns]; + NSArray *indexes = [self getTableSelectedInfo:JAVA_AX_COLS]; NSMutableArray *columns = [NSMutableArray arrayWithCapacity:[indexes count]]; for (NSNumber *i in indexes) { [columns addObject:[[ColumnAccessibility alloc] initWithParent:self @@ -230,16 +211,29 @@ - (nullable NSArray *)accessibilitySelectedColumns return [NSArray arrayWithArray:columns]; } -/* Other optional NSAccessibilityTable Methods -- (nullable NSArray> *)accessibilityVisibleRows; -- (nullable NSArray *)accessibilityColumns; -- (nullable NSArray *)accessibilitySelectedColumns; -- (nullable NSArray *)accessibilityVisibleColumns; +- (NSInteger)accessibilityRowCount +{ + return [[self getTableInfo:JAVA_AX_ROWS] integerValue]; +} - - (nullable NSArray *)accessibilitySelectedCells; -- (nullable NSArray *)accessibilityVisibleCells; -- (nullable NSArray *)accessibilityRowHeaderUIElements; -- (nullable NSArray *)accessibilityColumnHeaderUIElements; - */ +- (NSInteger)accessibilityColumnCount +{ + return [[self getTableInfo:JAVA_AX_COLS] integerValue]; +} + +- (nullable NSArray *)accessibilitySelectedCells; +{ + NSArray *children = [super accessibilitySelectedChildren]; + NSMutableArray *cells = [NSMutableArray arrayWithCapacity:[children count]]; + for (CommonComponentAccessibility *child in children) { + [cells addObject:[[CellAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:child->fAccessible + withIndex:child->fIndex + withView:fView + withJavaRole:child->fJavaRole]]; + } + return [NSArray arrayWithArray:cells]; +} @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m index ef91fce8c702d..536739a7cac48 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -52,11 +52,6 @@ - (NSAccessibilitySubrole)accessibilitySubrole return NSAccessibilityTableRowSubrole; } -- (BOOL)isAccessibilityEnabled -{ - return YES; -} - - (NSArray *)accessibilityChildren { NSArray *children = [super accessibilityChildren]; @@ -73,9 +68,9 @@ - (NSArray *)accessibilityChildren jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; - NSUInteger childIndex = [self rowNumberInTable] * [(TableAccessibility *)parent accessibleColCount]; + NSUInteger childIndex = [self rowNumberInTable] * [(TableAccessibility *)parent accessibilityColumnCount]; NSInteger i = childIndex * 2; - NSInteger n = ([self rowNumberInTable] + 1) * [(TableAccessibility *)parent accessibleColCount] * 2; + NSInteger n = ([self rowNumberInTable] + 1) * [(TableAccessibility *)parent accessibilityColumnCount] * 2; for(i; i < n; i+=2) { jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); @@ -97,7 +92,7 @@ - (NSArray *)accessibilityChildren withIndex:childIndex withView:self->fView withJavaRole:childJavaRole]; - [childrenCells addObject:[child autorelease]]; + [childrenCells addObject:[[child retain] autorelease]]; (*env)->DeleteLocalRef(env, jchild); (*env)->DeleteLocalRef(env, jchildJavaRole); @@ -113,7 +108,7 @@ - (NSArray *)accessibilityChildren - (NSInteger)accessibilityIndex { - return [[self accessibilityParent] accessibilityIndexOfChild:self]; + return self->fIndex; } - (NSString *)accessibilityLabel diff --git a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java index 2879eac00ede3..a5205191cdddc 100644 --- a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java @@ -25,7 +25,7 @@ /* * @test * @bug 8264287 - * @summary Create implementation for NSAccessibilityComboBox protocol peer + * @summary Test implementation of NSAccessibilityComboBox protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJComboboxTest * @requires (os.family == "windows" | os.family == "mac") diff --git a/test/jdk/java/awt/a11y/AccessibleJListTest.java b/test/jdk/java/awt/a11y/AccessibleJListTest.java index dac5e1524cd3d..67a499f85d637 100644 --- a/test/jdk/java/awt/a11y/AccessibleJListTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJListTest.java @@ -25,7 +25,7 @@ /* * @test * @bug 8264292 - * @summary Create implementation for NSAccessibilityList protocol peer + * @summary Test implementation of NSAccessibilityList protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJListTest * @requires (os.family == "windows" | os.family == "mac") diff --git a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java index cbf4bfe89bb4a..e6fc2a61aa749 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java @@ -25,7 +25,7 @@ /* * @test * @bug 8264303 - * @summary Create implementation for NSAccessibilityTabGroup protocol peer + * @summary Test implementation of NSAccessibilityTabPanel protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJTabbedPaneTest * @requires (os.family == "windows" | os.family == "mac") diff --git a/test/jdk/java/awt/a11y/AccessibleJTableTest.java b/test/jdk/java/awt/a11y/AccessibleJTableTest.java index ed52be7fdfb41..1c4b624e9f3ad 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTableTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTableTest.java @@ -25,9 +25,10 @@ /* * @test * @bug 8267388 - * @summary Create implementation for NSAccessibilityTable protocol + * @summary Test implementation of NSAccessibilityTable protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJTableTest + * @requires (os.family == "windows" | os.family == "mac") */ import javax.swing.*; diff --git a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java index d6d415d072f0a..fa9fab03110f5 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java @@ -25,7 +25,7 @@ /* * @test * @bug 8267387 - * @summary Create implementation for NSAccessibilityOutline protocol + * @summary Test implementation of NSAccessibilityOutLine protocol peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleJTreeTest * @requires (os.family == "windows" | os.family == "mac") diff --git a/test/jdk/java/awt/a11y/AccessibleTextTest.java b/test/jdk/java/awt/a11y/AccessibleTextTest.java index f70a1625d6292..4057f0926b28e 100644 --- a/test/jdk/java/awt/a11y/AccessibleTextTest.java +++ b/test/jdk/java/awt/a11y/AccessibleTextTest.java @@ -25,7 +25,7 @@ /* * @test * @bug 8262031 - * @summary Create implementation for NSAccessibilityNavigableStaticText protocol + * @summary Test implementation of NSAccessibilityNavigableStaticTest and NSAccessibilityStaticText protocols peer * @author Artem.Semenov@jetbrains.com * @run main/manual AccessibleTextTest * @requires (os.family == "windows" | os.family == "mac") From fbe34430d5050b42dc7c2bfecba9c1926d11b18b Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Wed, 14 Jul 2021 14:14:47 +0300 Subject: [PATCH 10/15] Why not mark them as @Native in the CAccessibility and use constants generated by the javac? --- .../classes/sun/lwawt/macosx/CAccessibility.java | 11 ++++++----- .../libawt_lwawt/awt/a11y/ColumnAccessibility.m | 3 ++- .../awt/a11y/CommonComponentAccessibility.h | 6 ------ .../awt/a11y/CommonComponentAccessibility.m | 10 ++++++---- .../libawt_lwawt/awt/a11y/GroupAccessibility.m | 3 ++- .../libawt_lwawt/awt/a11y/OutlineRowAccessibility.m | 3 ++- .../libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m | 5 +++-- .../libawt_lwawt/awt/a11y/TabGroupAccessibility.m | 7 ++++--- .../libawt_lwawt/awt/a11y/TableAccessibility.m | 12 +++++------- .../libawt_lwawt/awt/a11y/TableRowAccessibility.m | 3 ++- 10 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index 77d027ec98371..a3d8e6ae0ffcc 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -35,6 +35,7 @@ import java.awt.EventQueue; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.annotation.Native; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; @@ -654,9 +655,9 @@ public boolean[] call() throws Exception { // Duplicated from JavaComponentAccessibility // Note that values >=0 are indexes into the child array - static final int JAVA_AX_ALL_CHILDREN = -1; - static final int JAVA_AX_SELECTED_CHILDREN = -2; - static final int JAVA_AX_VISIBLE_CHILDREN = -3; + @Native static final int JAVA_AX_ALL_CHILDREN = -1; + @Native static final int JAVA_AX_SELECTED_CHILDREN = -2; + @Native static final int JAVA_AX_VISIBLE_CHILDREN = -3; // Each child takes up two entries in the array: one for itself and one for its role public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { @@ -749,8 +750,8 @@ public Object[] call() throws Exception { }, c); } - private static final int JAVA_AX_ROWS = 1; - private static final int JAVA_AX_COLS = 2; + @Native private static final int JAVA_AX_ROWS = 1; + @Native private static final int JAVA_AX_COLS = 2; public static int getTableInfo(final Accessible a, final Component c, final int info) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m index 2269128061f61..91db2b62953c0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -30,6 +30,7 @@ #import "TableAccessibility.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" static jclass sjc_CAccessibility = NULL; @@ -57,7 +58,7 @@ - (NSArray *)accessibilityChildren if (parent->fAccessible == NULL) return nil; GET_CHILDRENANDROLES_METHOD_RETURN(nil); jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, - parent->fAccessible, parent->fComponent, JAVA_AX_ALL_CHILDREN, NO); + parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); CHECK_EXCEPTION(); if (jchildrenAndRoles == NULL) return nil; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index 8e88539c11adf..0e946b4a67fc3 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -31,12 +31,6 @@ #import #import "JavaAccessibilityUtilities.h" -// these constants are duplicated in CAccessibility.java -#define JAVA_AX_ALL_CHILDREN (-1) -#define JAVA_AX_SELECTED_CHILDREN (-2) -#define JAVA_AX_VISIBLE_CHILDREN (-3) -// If the value is >=0, it's an index - @interface CommonComponentAccessibility : NSAccessibilityElement { NSView *fView; NSObject *fParent; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 45cd637747ca1..832432aa89422 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -33,6 +33,8 @@ #import "JNIUtilities.h" #import "AWTView.h" #import "sun_lwawt_macosx_CAccessible.h" +#import "sun_lwawt_macosx_CAccessibility.h" + // GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared. static jclass sjc_CAccessibility = NULL; @@ -747,17 +749,17 @@ - (NSString *)accessibilityHelp - (NSArray *)accessibilityChildren { - return [self accessibleChildrenWithChildCode:JAVA_AX_ALL_CHILDREN]; + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN]; } - (NSArray *)accessibilitySelectedChildren { - return [self accessibleChildrenWithChildCode:JAVA_AX_SELECTED_CHILDREN]; + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_SELECTED_CHILDREN]; } - (NSArray *)accessibilityVisibleChildren { - return [self accessibleChildrenWithChildCode:JAVA_AX_VISIBLE_CHILDREN]; + return [self accessibleChildrenWithChildCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_VISIBLE_CHILDREN]; } - (NSRect)accessibilityFrame @@ -1015,7 +1017,7 @@ - (id)accessibilityValue NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env - withChildrenCode:JAVA_AX_ALL_CHILDREN + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] > 0) { // handle case of AXMenuItem diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m index ff5450a4beff8..f3e0ee1f092c9 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m @@ -26,6 +26,7 @@ #import "GroupAccessibility.h" #import "JNIUtilities.h" #import "ThreadUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" /* * This is the protocol for the components that contain children. * Basic logic of accessibilityChildren might be overridden in the specific implementing @@ -45,7 +46,7 @@ - (NSArray *)accessibilityChildren { NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env - withChildrenCode:JAVA_AX_ALL_CHILDREN + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; if ([children count] == 0) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m index f1d8649f2e600..a2bce0bf70152 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m @@ -28,6 +28,7 @@ #import "ThreadUtilities.h" #import "JNIUtilities.h" #import "OutlineAccessibility.h" +#import "sun_lwawt_macosx_CAccessibility.h" static jclass sjc_CAccessible = NULL; #define GET_CACCESSIBLE_CLASS_RETURN(ret) \ @@ -66,7 +67,7 @@ - (NSArray *)accessibilityChildren jobject currentAccessible = [self currentAccessibleWithENV:env]; if (currentAccessible != NULL) { CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:currentAccessible withEnv:env withView:self->fView isCurrent:YES]; - NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] != 0) { return children; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m index 537dc039670c2..d3e2d352db693 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ScrollAreaAccessibility.m @@ -26,6 +26,7 @@ #import "ScrollAreaAccessibility.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" /* * Implementation of the accessibility peer for the ScrollArea role @@ -35,7 +36,7 @@ @implementation ScrollAreaAccessibility - (NSArray * _Nullable)accessibilityContentsAttribute { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; NSArray *contents = [NSMutableArray arrayWithCapacity:[children count]]; @@ -56,7 +57,7 @@ - (id _Nullable)getScrollBarwithOrientation:(enum NSAccessibilityOrientation)ori { JNIEnv *env = [ThreadUtilities getJNIEnv]; - NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:JAVA_AX_ALL_CHILDREN allowIgnored:YES]; + NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES]; if ([children count] <= 0) return nil; // The scroll bars are in the children. diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m index 822bec2db3c09..76fafc0faafda 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabGroupAccessibility.m @@ -27,6 +27,7 @@ #import "../JavaAccessibilityUtilities.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" static jclass sjc_CAccessibility = NULL; @@ -40,7 +41,7 @@ @implementation TabGroupAccessibility - (id)currentTabWithEnv:(JNIEnv *)env withAxContext:(jobject)axContext { - NSArray *tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + NSArray *tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; // Looking at the JTabbedPane sources, there is always one AccessibleSelection. jobject selAccessible = getAxContextSelection(env, axContext, 0, fComponent); @@ -127,7 +128,7 @@ - (NSArray *)accessibilityTabs { JNIEnv *env = [ThreadUtilities getJNIEnv]; jobject axContext = [self axContextWithEnv:env]; - id tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + id tabs = [self tabButtonsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; (*env)->DeleteLocalRef(env, axContext); return tabs; } @@ -136,7 +137,7 @@ - (NSArray *)accessibilityContents { JNIEnv *env = [ThreadUtilities getJNIEnv]; jobject axContext = [self axContextWithEnv:env]; - NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:JAVA_AX_ALL_CHILDREN allowIgnored:NO]; + NSArray* cont = [self contentsWithEnv:env withTabGroupAxContext:axContext withTabCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:NO]; (*env)->DeleteLocalRef(env, axContext); return cont; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m index d38a3faeca745..911191085629a 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -32,9 +32,7 @@ #import "ThreadUtilities.h" #import "JNIUtilities.h" #import "CellAccessibility.h" - -#define JAVA_AX_ROWS (1) -#define JAVA_AX_COLS (2) +#import "sun_lwawt_macosx_CAccessibility.h" static jclass sjc_CAccessibility = NULL; @@ -153,7 +151,7 @@ - (NSArray *)accessibilityRows - (nullable NSArray> *)accessibilitySelectedRows { - NSArray *selectedRowIndexses = [self getTableSelectedInfo:JAVA_AX_ROWS]; + NSArray *selectedRowIndexses = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS]; NSMutableArray *children = [NSMutableArray arrayWithCapacity:[selectedRowIndexses count]]; for (NSNumber *index in selectedRowIndexses) { [children addObject:[[TableRowAccessibility alloc] initWithParent:self @@ -198,7 +196,7 @@ - (nullable NSArray *)accessibilityColumns - (nullable NSArray *)accessibilitySelectedColumns { - NSArray *indexes = [self getTableSelectedInfo:JAVA_AX_COLS]; + NSArray *indexes = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS]; NSMutableArray *columns = [NSMutableArray arrayWithCapacity:[indexes count]]; for (NSNumber *i in indexes) { [columns addObject:[[ColumnAccessibility alloc] initWithParent:self @@ -213,12 +211,12 @@ - (nullable NSArray *)accessibilitySelectedColumns - (NSInteger)accessibilityRowCount { - return [[self getTableInfo:JAVA_AX_ROWS] integerValue]; + return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS] integerValue]; } - (NSInteger)accessibilityColumnCount { - return [[self getTableInfo:JAVA_AX_COLS] integerValue]; + return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS] integerValue]; } - (nullable NSArray *)accessibilitySelectedCells; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m index 536739a7cac48..19a32a419ac48 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -29,6 +29,7 @@ #import "CellAccessibility.h" #import "ThreadUtilities.h" #import "JNIUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" static jclass sjc_CAccessibility = NULL; @@ -61,7 +62,7 @@ - (NSArray *)accessibilityChildren if (parent->fAccessible == NULL) return nil; GET_CHILDRENANDROLES_METHOD_RETURN(nil); jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, - parent->fAccessible, parent->fComponent, JAVA_AX_ALL_CHILDREN, NO); + parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); CHECK_EXCEPTION(); if (jchildrenAndRoles == NULL) return nil; From 76cba143ea0c0fe118982e55d5b06c78429dd088 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Thu, 15 Jul 2021 14:59:51 +0300 Subject: [PATCH 11/15] ok, so this is in the new code, how hard it will be to reimplement these methods to call invokeAndWait only once per callback from the native, as it usually was done before? --- .../sun/lwawt/macosx/CAccessibility.java | 172 +++++++++++------- 1 file changed, 108 insertions(+), 64 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index a3d8e6ae0ffcc..774897221c1af 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -32,7 +32,6 @@ import java.awt.Point; import java.awt.Window; import java.awt.event.KeyEvent; -import java.awt.EventQueue; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.annotation.Native; @@ -128,7 +127,7 @@ static T invokeAndWait(final Callable callable, final Component c, final T value = null; if (c != null) { try { - value = EventQueue.isDispatchThread() ? callable.call() : LWCToolkit.invokeAndWait(callable, c); + value = LWCToolkit.invokeAndWait(callable, c); } catch (final Exception e) { e.printStackTrace(); } } @@ -664,64 +663,68 @@ public static Object[] getChildrenAndRoles(final Accessible a, final Component c if (a == null) return null; return invokeAndWait(new Callable() { public Object[] call() throws Exception { - ArrayList childrenAndRoles = new ArrayList(); - _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); - - /* In the case of fetching a selection, need to check to see if - * the active descendant is at the beginning of the list. If it - * is not it needs to be moved to the beginning of the list so - * VoiceOver will annouce it correctly. The list returned - * from Java is always in order from top to bottom, but when shift - * selecting downward (extending the list) or multi-selecting using - * the VO keys control+option+command+return the active descendant - * is not at the top of the list in the shift select down case and - * may not be in the multi select case. - */ - if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { - if (!childrenAndRoles.isEmpty()) { - AccessibleContext activeDescendantAC = - CAccessible.getActiveDescendant(a); - if (activeDescendantAC != null) { - String activeDescendantName = - activeDescendantAC.getAccessibleName(); - AccessibleRole activeDescendantRole = - activeDescendantAC.getAccessibleRole(); - // Move active descendant to front of list. - // List contains pairs of each selected item's - // Accessible and AccessibleRole. - ArrayList newArray = new ArrayList(); - int count = childrenAndRoles.size(); - Accessible currentAccessible = null; - AccessibleContext currentAC = null; - String currentName = null; - AccessibleRole currentRole = null; - for (int i = 0; i < count; i+=2) { - // Is this the active descendant? - currentAccessible = (Accessible)childrenAndRoles.get(i); - currentAC = currentAccessible.getAccessibleContext(); - currentName = currentAC.getAccessibleName(); - currentRole = (AccessibleRole)childrenAndRoles.get(i+1); - if (currentName != null && currentName.equals(activeDescendantName) && - currentRole.equals(activeDescendantRole) ) { - newArray.add(0, currentAccessible); - newArray.add(1, currentRole); - } else { - newArray.add(currentAccessible); - newArray.add(currentRole); - } - } - childrenAndRoles = newArray; + return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored); + } + }, c); + } + + private static Object[] getChildrenAndRolesImpl(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { + if (a == null) return null; + + ArrayList childrenAndRoles = new ArrayList(); + _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); + + /* In case of fetching a selection, we need to check if +* the active descendant is at the beginning of the list, or +* otherwise move it, so that VoiceOver announces it correctly. + * The java list is always in order from top to bottom, but when + * (1) shift-selecting downward (extending the list) or (2) multi-selecting with + * the VO keys (CTRL+ALT+CMD+RETURN) the active descendant + * is not at the top of the list in the 1st case and may not be in the 2nd. + */ + if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { + if (!childrenAndRoles.isEmpty()) { + AccessibleContext activeDescendantAC = + CAccessible.getActiveDescendant(a); + if (activeDescendantAC != null) { + String activeDescendantName = + activeDescendantAC.getAccessibleName(); + AccessibleRole activeDescendantRole = + activeDescendantAC.getAccessibleRole(); + // Move active descendant to front of list. + // List contains pairs of each selected item's + // Accessible and AccessibleRole. + ArrayList newArray = new ArrayList(); + int count = childrenAndRoles.size(); + Accessible currentAccessible = null; + AccessibleContext currentAC = null; + String currentName = null; + AccessibleRole currentRole = null; + for (int i = 0; i < count; i += 2) { + // Is this the active descendant? + currentAccessible = (Accessible) childrenAndRoles.get(i); + currentAC = currentAccessible.getAccessibleContext(); + currentName = currentAC.getAccessibleName(); + currentRole = (AccessibleRole) childrenAndRoles.get(i + 1); + if (currentName != null && currentName.equals(activeDescendantName) && + currentRole.equals(activeDescendantRole)) { + newArray.add(0, currentAccessible); + newArray.add(1, currentRole); + } else { + newArray.add(currentAccessible); + newArray.add(currentRole); } } + childrenAndRoles = newArray; } + } + } - if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) { - return childrenAndRoles.toArray(); - } + if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) { + return childrenAndRoles.toArray(); + } - return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) }; - } - }, c); + return new Object[]{childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1)}; } // This method is called from the native @@ -731,20 +734,61 @@ private static Object[] getChildrenAndRolesRecursive(final Accessible a, final C return invokeAndWait(new Callable() { public Object[] call() throws Exception { ArrayList currentLevelChildren = new ArrayList(); - currentLevelChildren.addAll(Arrays.asList(getChildrenAndRoles(a, c, JAVA_AX_ALL_CHILDREN, allowIgnored))); ArrayList allChildren = new ArrayList(); - for (int i = 0; i < currentLevelChildren.size(); i += 2) { - if ((((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) || - (((Accessible) currentLevelChildren.get(i)).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) || + ArrayList parentStack = new ArrayList(); + parentStack.add(a); + ArrayList indexses = new ArrayList(); + Integer index = 0; + int currentLevel = level; + while (!parentStack.isEmpty()) { + Accessible p = parentStack.get(parentStack.size() - 1); + + currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored))); + if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) { + if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1); + if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1); + currentLevel -= 1; + currentLevelChildren.clear(); + continue; + } + + Accessible ca = null; + Object obj = currentLevelChildren.get(index); + if (!(obj instanceof Accessible)) { + index += 2; + currentLevelChildren.clear(); + continue; + } + ca = (Accessible) obj; + Object role = currentLevelChildren.get(index + 1); + currentLevelChildren.clear(); + + AccessibleContext cac = ca.getAccessibleContext(); + if (cac == null) { + index += 2; + continue; + } + + if ((cac.getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) || + (cac.getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) || (whichChildren == JAVA_AX_ALL_CHILDREN)) { - allChildren.add(currentLevelChildren.get(i)); - allChildren.add(currentLevelChildren.get(i + 1)); - allChildren.add(String.valueOf(level)); + allChildren.add(ca); + allChildren.add(role); + allChildren.add(String.valueOf(currentLevel)); } - if (getAccessibleStateSet(((Accessible) currentLevelChildren.get(i)).getAccessibleContext(), c).contains(AccessibleState.EXPANDED)) { - allChildren.addAll(Arrays.asList(getChildrenAndRolesRecursive(((Accessible) currentLevelChildren.get(i)), c, whichChildren, allowIgnored, level + 1))); + + index += 2; + + if (cac.getAccessibleStateSet().contains(AccessibleState.EXPANDED)) { + parentStack.add(ca); + indexses.add(index); + index = 0; + currentLevel += 1; + continue; } + } + return allChildren.toArray(); } }, c); From b4ef57a87a45b7bb5b8ed06d7efa687dbc192469 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Tue, 20 Jul 2021 11:29:28 +0300 Subject: [PATCH 12/15] 1. Removing the warning about an unaccessible element in the console. 2. Fixed a potential problem with memory in table cells; 3. Fixed a problem with the sounding of cell coordinates. --- .../libawt_lwawt/awt/a11y/CellAccessibility.m | 23 ++++++++++++++++++- .../awt/a11y/CommonComponentAccessibility.m | 4 +--- .../awt/a11y/ListRowAccessibility.m | 2 ++ .../awt/a11y/TableAccessibility.m | 6 ++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m index 0ebb28861de5d..f3dc90a59a450 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m @@ -24,6 +24,7 @@ #import "CellAccessibility.h" #import "ThreadUtilities.h" +#import "TableAccessibility.h" @implementation CellAccessibility @@ -45,7 +46,7 @@ - (NSArray *)accessibilityChildren index:self->fIndex withEnv:[ThreadUtilities getJNIEnv] withView:self->fView - isWrapped:YES]; + isWrapped:NO]; return [NSArray arrayWithObject:newChild]; } else { return children; @@ -62,4 +63,24 @@ - (id)accessibilityParent return [super accessibilityParent]; } +- (NSRange)accessibilityRowIndexRange { + NSInteger location = -1; + if ([[(CommonComponentAccessibility *)fParent accessibilityParent] isKindOfClass:[TableAccessibility class]]) { + TableAccessibility *table = [(CommonComponentAccessibility *)fParent accessibilityParent]; + location = [table accessibleRowAtIndex:fIndex]; + } + + return NSMakeRange(location, 0); +} + +- (NSRange)accessibilityColumnIndexRange { + NSInteger location = -1; + if ([[(CommonComponentAccessibility *)fParent accessibilityParent] isKindOfClass:[TableAccessibility class]]) { + TableAccessibility *table = [(CommonComponentAccessibility *)fParent accessibilityParent]; + location = [table accessibleColumnAtIndex:fIndex]; + } + + return NSMakeRange(location, 0); +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 832432aa89422..1b23c6f6b3dbf 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -794,9 +794,7 @@ - (BOOL)isAccessibilityEnabled BOOL value = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, jm_isEnabled, fAccessible, fComponent); CHECK_EXCEPTION(); - if (!value) { - NSLog(@"WARNING: %s called on component that has no accessible component: %@", __FUNCTION__, self); - } + return value; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m index 9212b181aeb50..2c4d51e10ffab 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m @@ -42,6 +42,8 @@ - (NSArray *)accessibilityChildren { NSArray *children = [super accessibilityChildren]; if (children == NULL) { + + // Since the row element has already been created, we should no create it again, but just retrieve it by a pointer, that's why isWrapped is set to YES. CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self accessible:self->fAccessible role:self->fJavaRole diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m index 911191085629a..cd534c1ad3ccb 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -219,7 +219,7 @@ - (NSInteger)accessibilityColumnCount return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS] integerValue]; } -- (nullable NSArray *)accessibilitySelectedCells; +- (nullable NSArray *)accessibilitySelectedCells { NSArray *children = [super accessibilitySelectedChildren]; NSMutableArray *cells = [NSMutableArray arrayWithCapacity:[children count]]; @@ -234,4 +234,8 @@ - (nullable NSArray *)accessibilitySelectedCells; return [NSArray arrayWithArray:cells]; } +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row { + return [[(TableRowAccessibility *)[[self accessibilityRows] objectAtIndex:row] accessibilityChildren] objectAtIndex:column]; +} + @end From a07c86d917c22fcad1cca9cacea83fb3880ec52e Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Tue, 20 Jul 2021 16:15:00 +0300 Subject: [PATCH 13/15] Wildcard imports are avoided in new tests --- .../awt/a11y/AccessibleComponentTest.java | 6 ++++- .../awt/a11y/AccessibleJComboboxTest.java | 7 +++-- .../java/awt/a11y/AccessibleJListTest.java | 27 +++++++++++++++---- .../awt/a11y/AccessibleJTabbedPaneTest.java | 9 ++++++- .../java/awt/a11y/AccessibleJTableTest.java | 8 ++++-- .../java/awt/a11y/AccessibleJTreeTest.java | 11 ++++++-- .../jdk/java/awt/a11y/AccessibleTextTest.java | 11 ++++++-- 7 files changed, 64 insertions(+), 15 deletions(-) diff --git a/test/jdk/java/awt/a11y/AccessibleComponentTest.java b/test/jdk/java/awt/a11y/AccessibleComponentTest.java index de258727f4ca3..86d41574d6e34 100644 --- a/test/jdk/java/awt/a11y/AccessibleComponentTest.java +++ b/test/jdk/java/awt/a11y/AccessibleComponentTest.java @@ -31,8 +31,12 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import javax.swing.*; +import java.awt.GridBagLayout; + import javax.swing.JPanel; +import javax.swing.JFrame; +import javax.swing.JTextArea; +import javax.swing.JButton; public abstract class AccessibleComponentTest { diff --git a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java index a5205191cdddc..a9b29d5310ceb 100644 --- a/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJComboboxTest.java @@ -31,8 +31,11 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; -import java.awt.*; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import java.awt.FlowLayout; import java.util.concurrent.CountDownLatch; public class AccessibleJComboboxTest extends AccessibleComponentTest { diff --git a/test/jdk/java/awt/a11y/AccessibleJListTest.java b/test/jdk/java/awt/a11y/AccessibleJListTest.java index 67a499f85d637..ae1dbce9b71b0 100644 --- a/test/jdk/java/awt/a11y/AccessibleJListTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJListTest.java @@ -31,14 +31,31 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import java.awt.*; -import java.awt.event.*; +import javax.swing.JList; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JWindow; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.ListCellRenderer; +import javax.swing.SwingUtilities; +import javax.swing.Popup; +import javax.swing.PopupFactory; + +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.Rectangle; +import java.awt.Dimension; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Window; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.awt.*; public class AccessibleJListTest extends AccessibleComponentTest { diff --git a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java index e6fc2a61aa749..f03af5d3a0975 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTabbedPaneTest.java @@ -31,7 +31,14 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; +import javax.swing.JTabbedPane; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTextField; +import javax.swing.JCheckBox; +import javax.swing.SwingUtilities; + import java.util.concurrent.CountDownLatch; public class AccessibleJTabbedPaneTest extends AccessibleComponentTest { diff --git a/test/jdk/java/awt/a11y/AccessibleJTableTest.java b/test/jdk/java/awt/a11y/AccessibleJTableTest.java index 1c4b624e9f3ad..209b81a58861f 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTableTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTableTest.java @@ -31,8 +31,12 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; -import java.awt.*; +import javax.swing.JTable; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +import java.awt.FlowLayout; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java index fa9fab03110f5..ab4c131b0ac0a 100644 --- a/test/jdk/java/awt/a11y/AccessibleJTreeTest.java +++ b/test/jdk/java/awt/a11y/AccessibleJTreeTest.java @@ -31,10 +31,17 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; +import javax.swing.JTree; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JScrollPane; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellRenderer; -import java.awt.*; +import javax.swing.SwingUtilities; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; import java.util.Hashtable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/test/jdk/java/awt/a11y/AccessibleTextTest.java b/test/jdk/java/awt/a11y/AccessibleTextTest.java index 4057f0926b28e..542d7bde0825b 100644 --- a/test/jdk/java/awt/a11y/AccessibleTextTest.java +++ b/test/jdk/java/awt/a11y/AccessibleTextTest.java @@ -31,8 +31,15 @@ * @requires (os.family == "windows" | os.family == "mac") */ -import javax.swing.*; -import java.awt.*; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; + +import java.awt.FlowLayout; import java.awt.event.KeyEvent; import java.awt.event.KeyAdapter; import java.beans.PropertyChangeEvent; From 65053348b9f4b5cb5c7a7fb785bdcac5348d8104 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Wed, 21 Jul 2021 16:14:41 +0300 Subject: [PATCH 14/15] 1. The comment states that it is not necessary to call ExceptionCheck, but it will be done before by the CHECK_EXCEPTION. The macro also will log it when necessary. 2. What happens if "fAccessible" and "fComponent" will be null due to OOM, should we rethrow this exception, or ignoring it is fine? --- .../libawt_lwawt/awt/a11y/CommonComponentAccessibility.m | 9 +++++++-- .../libawt_lwawt/awt/a11y/NavigableTextAccessibility.m | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 1b23c6f6b3dbf..9fe7ce4e52040 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -256,10 +256,15 @@ - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jo fView = [view retain]; fJavaRole = [javaRole retain]; - fAccessible = (*env)->NewWeakGlobalRef(env, accessible); - (*env)->ExceptionClear(env); // in case of OOME + if (accessible != NULL) { + fAccessible = (*env)->NewWeakGlobalRef(env, accessible); + CHECK_EXCEPTION(); + } + jobject jcomponent = [(AWTView *)fView awtComponent:env]; fComponent = (*env)->NewWeakGlobalRef(env, jcomponent); + CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, jcomponent); fIndex = index; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m index 11802b6cb8e51..62ad836da65b4 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m @@ -73,7 +73,7 @@ - (NSRect)accessibilityFrameForRange:(NSRange)range // We cheat because we know that the array is 4 elements long (x, y, width, height) jdouble *values = (*env)->GetDoubleArrayElements(env, axBounds, 0); - CHECK_EXCEPTION(); + if (values == NULL) { // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. NSLog(@"%s failed calling GetDoubleArrayElements", __FUNCTION__); From a13d6a6b08d86c482c86360d9880ee11147c45d6 Mon Sep 17 00:00:00 2001 From: Artem Semenov Date: Mon, 2 Aug 2021 11:50:36 +0300 Subject: [PATCH 15/15] 1. Fixed problems with vertical navigation in table cells; 2. I suggest doing the opposite, leave the CHECK_EXCEPTION and remove the chech and the logging. The CHECK_EXCEPTION is better since it can be configured via properties, and it will chech itself on what thread the current code is executed. --- .../native/libawt_lwawt/awt/a11y/CellAccessibility.m | 4 ++-- .../native/libawt_lwawt/awt/a11y/ColumnAccessibility.m | 6 +++--- .../libawt_lwawt/awt/a11y/NavigableTextAccessibility.m | 6 +----- .../libawt_lwawt/awt/a11y/TableRowAccessibility.m | 10 ++-------- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m index f3dc90a59a450..688d8eb3176b2 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m @@ -70,7 +70,7 @@ - (NSRange)accessibilityRowIndexRange { location = [table accessibleRowAtIndex:fIndex]; } - return NSMakeRange(location, 0); + return NSMakeRange(location, 1); } - (NSRange)accessibilityColumnIndexRange { @@ -80,7 +80,7 @@ - (NSRange)accessibilityColumnIndexRange { location = [table accessibleColumnAtIndex:fIndex]; } - return NSMakeRange(location, 0); + return NSMakeRange(location, 1); } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m index 91db2b62953c0..9d4582445a471 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -65,7 +65,7 @@ - (NSArray *)accessibilityChildren jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; - NSUInteger childIndex = [self columnNumberInTable]; + NSUInteger childIndex = fIndex; int inc = [(TableAccessibility *)[self accessibilityParent] accessibilityRowCount] * 2; NSInteger i = childIndex * 2; @@ -104,9 +104,9 @@ - (NSArray *)accessibilityChildren } } -- (NSUInteger)columnNumberInTable +- (NSInteger)accessibilityIndex { - return self->fIndex; + return fIndex; } @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m index 62ad836da65b4..84d8785e20541 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m @@ -73,12 +73,8 @@ - (NSRect)accessibilityFrameForRange:(NSRange)range // We cheat because we know that the array is 4 elements long (x, y, width, height) jdouble *values = (*env)->GetDoubleArrayElements(env, axBounds, 0); + CHECK_EXCEPTION(); - if (values == NULL) { - // Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck. - NSLog(@"%s failed calling GetDoubleArrayElements", __FUNCTION__); - return NSMakeRect(0, 0, 0, 0); - }; NSRect bounds; bounds.origin.x = values[0]; bounds.origin.y = [[[[self view] window] screen] frame].size.height - values[1] - values[3]; //values[1] is y-coord from top-left of screen. Flip. Account for the height (values[3]) when flipping diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m index 19a32a419ac48..60985e203f9a6 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -69,9 +69,9 @@ - (NSArray *)accessibilityChildren jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; - NSUInteger childIndex = [self rowNumberInTable] * [(TableAccessibility *)parent accessibilityColumnCount]; + NSUInteger childIndex = fIndex * [(TableAccessibility *)parent accessibilityColumnCount]; NSInteger i = childIndex * 2; - NSInteger n = ([self rowNumberInTable] + 1) * [(TableAccessibility *)parent accessibilityColumnCount] * 2; + NSInteger n = (fIndex + 1) * [(TableAccessibility *)parent accessibilityColumnCount] * 2; for(i; i < n; i+=2) { jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); @@ -131,11 +131,6 @@ - (id)accessibilityParent return [super accessibilityParent]; } -- (NSUInteger)rowNumberInTable -{ - return (NSUInteger)self->fIndex; -} - - (NSRect)accessibilityFrame { int height = [[[self accessibilityChildren] objectAtIndex:0] accessibilityFrame].size.height; @@ -148,4 +143,3 @@ - (NSRect)accessibilityFrame } @end -