# HG changeset patch # User ptbrunet # Date 1475803919 18000 # Node ID 9deca564da25f60f3b0e2fd886aece23ca5fe0f8 # Parent fef33230eaf897c38c52ccaf94992cf12bc2838d 8160893: [macosx] JMenuItems in JPopupMenu are not accessible Summary: post events for MenuOpened/Closed/ItemSelected Reviewed-by: ant, alexsch diff -r fef33230eaf8 -r 9deca564da25 jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java --- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Thu Oct 06 11:39:20 2016 -0700 +++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java Thu Oct 06 20:31:59 2016 -0500 @@ -29,11 +29,9 @@ import java.awt.*; import java.beans.*; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.Callable; -import sun.awt.AWTAccessor; import javax.accessibility.*; import javax.swing.*; @@ -73,8 +71,20 @@ } public void propertyChange(final PropertyChangeEvent evt) { - if (evt.getNewValue() == null) return; - focusChanged(); + Object newValue = evt.getNewValue(); + if (newValue == null) return; + // Don't post focus on things that don't matter, i.e. alert, colorchooser, + // desktoppane, dialog, directorypane, filechooser, filler, fontchoose, + // frame, glasspane, layeredpane, optionpane, panel, rootpane, separator, + // tooltip, viewport, window. + // List taken from initializeRoles() in JavaComponentUtilities.m. + if (newValue instanceof Accessible) { + AccessibleContext nvAC = ((Accessible) newValue).getAccessibleContext(); + AccessibleRole nvRole = nvAC.getAccessibleRole(); + if (!ignoredRoles.contains(roleKey(nvRole))) { + focusChanged(); + } + } } private native void focusChanged(); @@ -683,9 +693,15 @@ if (context == null) continue; if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) { - if (!context.getAccessibleComponent().isVisible()) continue; + AccessibleComponent acomp = context.getAccessibleComponent(); + if (acomp == null || !acomp.isVisible()) { + continue; + } } else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { - if (!ac.getAccessibleSelection().isAccessibleChildSelected(i)) continue; + AccessibleSelection sel = ac.getAccessibleSelection(); + if (sel == null || !sel.isAccessibleChildSelected(i)) { + continue; + } } if (!allowIgnored) { diff -r fef33230eaf8 -r 9deca564da25 jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java --- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Thu Oct 06 11:39:20 2016 -0700 +++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java Thu Oct 06 20:31:59 2016 -0500 @@ -39,7 +39,10 @@ import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY; import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY; +import static javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY; import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; import sun.awt.AWTAccessor; @@ -63,6 +66,9 @@ private static native void valueChanged(long ptr); private static native void selectedTextChanged(long ptr); private static native void selectionChanged(long ptr); + private static native void menuOpened(long ptr); + private static native void menuClosed(long ptr); + private static native void menuItemSelected(long ptr); private Accessible accessible; @@ -111,16 +117,45 @@ public void propertyChange(PropertyChangeEvent e) { String name = e.getPropertyName(); if ( ptr != 0 ) { + Object newValue = e.getNewValue(); + Object oldValue = e.getOldValue(); if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) { selectedTextChanged(ptr); } else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0 ) { valueChanged(ptr); } else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0 ) { selectionChanged(ptr); - } else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) { - Object nv = e.getNewValue(); - if (nv instanceof AccessibleContext) { - activeDescendant = (AccessibleContext)nv; + } else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) { + if (newValue instanceof AccessibleContext) { + activeDescendant = (AccessibleContext)newValue; + } + } else if (name.compareTo(ACCESSIBLE_STATE_PROPERTY) == 0) { + AccessibleContext thisAC = accessible.getAccessibleContext(); + AccessibleRole thisRole = thisAC.getAccessibleRole(); + Accessible parentAccessible = thisAC.getAccessibleParent(); + AccessibleRole parentRole = null; + if (parentAccessible != null) { + parentRole = parentAccessible.getAccessibleContext().getAccessibleRole(); + } + // 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 + // for JPopupMenus, not for combobox menus. + if (parentRole != AccessibleRole.COMBO_BOX) { + if (thisRole == AccessibleRole.POPUP_MENU) { + if ( newValue != null && + ((AccessibleState)newValue) == AccessibleState.VISIBLE ) { + menuOpened(ptr); + } else if ( oldValue != null && + ((AccessibleState)oldValue) == AccessibleState.VISIBLE ) { + menuClosed(ptr); + } + } else if (thisRole == AccessibleRole.MENU_ITEM) { + if ( newValue != null && + ((AccessibleState)newValue) == AccessibleState.FOCUSED ) { + menuItemSelected(ptr); + } + } } } } diff -r fef33230eaf8 -r 9deca564da25 jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m Thu Oct 06 11:39:20 2016 -0700 +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m Thu Oct 06 20:31:59 2016 -0500 @@ -66,7 +66,6 @@ static JNF_MEMBER_CACHE(jf_ptr, sjc_CAccessible, "ptr", "J"); static JNF_STATIC_MEMBER_CACHE(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;"); - static jobject sAccessibilityClass = NULL; // sAttributeNamesForRoleCache holds the names of the attributes to which each java @@ -213,6 +212,24 @@ NSAccessibilityPostNotification(self, NSAccessibilitySelectedChildrenChangedNotification); } +- (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; @@ -278,8 +295,7 @@ + (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env { if (JNFIsInstanceOf(env, jaccessible, &sjc_CAccessible)) { return jaccessible; - } - else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) { + } else if (JNFIsInstanceOf(env, jaccessible, &sjc_Accessible)) { return JNFCallStaticObjectMethod(env, sjm_getCAccessible, jaccessible); } return NULL; @@ -368,6 +384,14 @@ // 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]; JNFSetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); @@ -634,6 +658,15 @@ return moreNames; } } + // popupmenu's return values not selected children + if ( [javaRole isEqualToString:@"popupmenu"] && + ![[[self parent] javaRole] isEqualToString:@"combobox"] ) { + NSMutableArray *moreNames = + [[NSMutableArray alloc] initWithCapacity: [names count] + 1]; + [moreNames addObjectsFromArray: names]; + [moreNames addObject:NSAccessibilityValueAttribute]; + return moreNames; + } return names; } // end @synchronized @@ -707,6 +740,7 @@ return value; } + - (BOOL)accessibilityIsChildrenAttributeSettable { return NO; @@ -939,6 +973,13 @@ 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; @@ -947,6 +988,7 @@ } return fNSRole; } + - (BOOL)accessibilityIsRoleAttributeSettable { return NO; @@ -1046,8 +1088,7 @@ - (NSString *)accessibilitySubroleAttribute { NSString *value = nil; - if ([[self javaRole] isEqualToString:@"passwordtext"]) - { + if ([[self javaRole] isEqualToString:@"passwordtext"]) { value = NSAccessibilitySecureTextFieldSubrole; } /* @@ -1123,6 +1164,45 @@ 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 = + [JavaComponentAccessibility 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 accessibilitySelectedChildrenAttribute]; + JavaComponentAccessibility *selectedMenuItem = + [selectedChildrenOfMenu objectAtIndex:0]; + if (selectedMenuItem != nil) { + jobject itemValue = + JNFCallStaticObjectMethod( env, + sjm_getAccessibleName, + selectedMenuItem->fAccessible, + selectedMenuItem->fComponent ); // AWT_THREADING Safe (AWTRunLoop) + if (itemValue == NULL) { + return nil; + } + NSString* itemString = JNFJavaToNSString(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 @@ -1345,6 +1425,54 @@ /* * Class: sun_lwawt_macosx_CAccessible + * Method: menuOpened + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuOpened +(JNIEnv *env, jclass jklass, jlong element) +{ +JNF_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postMenuOpened) + on:(JavaComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: menuClosed + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuClosed +(JNIEnv *env, jclass jklass, jlong element) +{ +JNF_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postMenuClosed) + on:(JavaComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: menuItemSelected + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_menuItemSelected +(JNIEnv *env, jclass jklass, jlong element) +{ +JNF_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postMenuItemSelected) + on:(JavaComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CAccessible * Method: unregisterFromCocoaAXSystem * Signature: (I)V */