From 7d24974ab452e650afd5ed97e0e72455f020da65 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 12 Aug 2025 14:32:41 +0200 Subject: [PATCH] backport e33eeeea04fc7899bf66b0a2fdaccc30060854b4 --- .../sun/lwawt/macosx/CAccessibility.java | 32 ++++- .../libawt_lwawt/awt/a11y/MenuAccessibility.m | 33 +++++- .../TestPopupMenuChildCount.java | 109 ++++++++++++++++++ 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/accessibility/TestPopupMenuChildCount.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 0ab38fc06b4..6e808db606c 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. 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 @@ -60,9 +60,11 @@ import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; +import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JTextArea; import javax.swing.JList; +import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.KeyStroke; @@ -813,6 +815,34 @@ public Object[] call() throws Exception { }, c); } + private static Accessible getCurrentAccessiblePopupMenu(Accessible a, Component c) { + if (a == null) return null; + + return invokeAndWait(new Callable() { + @Override + public Accessible call() throws Exception { + return traversePopupMenu(a); + } + }, c); + } + + private static Accessible traversePopupMenu(Accessible a) { + // a is root level popupmenu + AccessibleContext ac = a.getAccessibleContext(); + if (ac != null) { + for (int i = 0; i < ac.getAccessibleChildrenCount(); i++) { + Accessible child = ac.getAccessibleChild(i); + if (child instanceof JMenu subMenu) { + JPopupMenu popup = subMenu.getPopupMenu(); + if (popup.isVisible()) { + return traversePopupMenu((Accessible) popup); + } + } + } + } + return a; + } + @Native private static final int JAVA_AX_ROWS = 1; @Native private static final int JAVA_AX_COLS = 2; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m index e8b28d93068..90a147aa5a2 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. 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 @@ -24,6 +24,10 @@ */ #import "MenuAccessibility.h" +#import "ThreadUtilities.h" +#import "sun_lwawt_macosx_CAccessibility.h" + +static jclass sjc_CAccessibility = NULL; /* * Implementing a protocol that represents menus both as submenu and as a @@ -51,4 +55,31 @@ - (id _Nullable)accessibilityValue return NULL; } +/* + * Return all non-ignored children. + */ +- (NSArray *)accessibilityChildren { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(sjm_getCurrentAccessiblePopupMenu, sjc_CAccessibility, + "getCurrentAccessiblePopupMenu", + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil); + jobject axComponent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, + sjm_getCurrentAccessiblePopupMenu, + fAccessible, fComponent); + + CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:axComponent + withEnv:env withView:self->fView isCurrent:YES]; + + NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement + withEnv:env + withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN + allowIgnored:NO]; + + if ([children count] == 0) { + return nil; + } else { + return children; + } +} @end diff --git a/test/jdk/javax/accessibility/TestPopupMenuChildCount.java b/test/jdk/javax/accessibility/TestPopupMenuChildCount.java new file mode 100644 index 00000000000..f59b7007145 --- /dev/null +++ b/test/jdk/javax/accessibility/TestPopupMenuChildCount.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. 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.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +/* + * @test + * @bug 8341311 + * @summary Verifies that VoiceOver announces correct number of child for PopupMenu on macOS + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestPopupMenuChildCount + */ + +public class TestPopupMenuChildCount { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is applicable only on macOS. + + Test UI contains an empty JFrame. On press of left/right mouse button, + a PopupMenu will be visible. + + Follow these steps to test the behaviour: + + 1. Start the VoiceOver (Press Command + F5) application + 2. Press Left/Right mouse button inside test frame window to open + the PopupMenu + 3. VO should announce "Menu" with number of child items of the Popupmenu + 4. Press Up/Down arrow to traverse popupmenu child items + 5. Press Right arrow key to open submenu + 6. VO should announce "Menu" with correct number of child items + for the submenu (For e.g. When Submenu-1 is open, VO should announce + "Menu 4 items") + 7. Repeat the process for other submenus + 8. Press Pass if you are able to hear correct announcements + else Fail"""; + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(TestPopupMenuChildCount::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame("Test Frame"); + + JPopupMenu popupmenu = new JPopupMenu(); + JMenuItem mi1 = new JMenuItem("MenuItem-1"); + JMenuItem mi2 = new JMenuItem("MenuItem-2"); + JMenuItem mi3 = new JMenuItem("MenuItem-3"); + popupmenu.add(mi1); + popupmenu.add(mi2); + popupmenu.add(mi3); + + JMenu submenu1 = new JMenu("Submenu-1"); + submenu1.add("subOne"); + submenu1.add("subTwo"); + submenu1.add("subThree"); + + JMenu submenu2 = new JMenu("Submenu-2"); + submenu2.add("subOne"); + submenu2.add("subTwo"); + + JMenu submenu3 = new JMenu ("Submenu-3"); + submenu3.add("subOne"); + submenu1.add(submenu3); + + popupmenu.add(submenu1); + popupmenu.add(submenu2); + + frame.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupmenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + frame.setSize(300, 300); + return frame; + } +}