Skip to content

Commit e57fbd4

Browse files
author
Satyen Subramaniam
committed
8341311: [Accessibility,macOS,VoiceOver] VoiceOver announces incorrect number of items in submenu of JPopupMenu
Backport-of: e33eeeea04fc7899bf66b0a2fdaccc30060854b4
1 parent b783173 commit e57fbd4

File tree

3 files changed

+172
-2
lines changed

3 files changed

+172
-2
lines changed

src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -60,9 +60,11 @@
6060
import javax.swing.JComponent;
6161
import javax.swing.JEditorPane;
6262
import javax.swing.JLabel;
63+
import javax.swing.JMenu;
6364
import javax.swing.JMenuItem;
6465
import javax.swing.JTextArea;
6566
import javax.swing.JList;
67+
import javax.swing.JPopupMenu;
6668
import javax.swing.JTree;
6769
import javax.swing.KeyStroke;
6870

@@ -862,6 +864,34 @@ public Accessible call() throws Exception {
862864
}, c);
863865
}
864866

867+
private static Accessible getCurrentAccessiblePopupMenu(Accessible a, Component c) {
868+
if (a == null) return null;
869+
870+
return invokeAndWait(new Callable<Accessible>() {
871+
@Override
872+
public Accessible call() throws Exception {
873+
return traversePopupMenu(a);
874+
}
875+
}, c);
876+
}
877+
878+
private static Accessible traversePopupMenu(Accessible a) {
879+
// a is root level popupmenu
880+
AccessibleContext ac = a.getAccessibleContext();
881+
if (ac != null) {
882+
for (int i = 0; i < ac.getAccessibleChildrenCount(); i++) {
883+
Accessible child = ac.getAccessibleChild(i);
884+
if (child instanceof JMenu subMenu) {
885+
JPopupMenu popup = subMenu.getPopupMenu();
886+
if (popup.isVisible()) {
887+
return traversePopupMenu((Accessible) popup);
888+
}
889+
}
890+
}
891+
}
892+
return a;
893+
}
894+
865895
@Native private static final int JAVA_AX_ROWS = 1;
866896
@Native private static final int JAVA_AX_COLS = 2;
867897

src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,10 @@
2424
*/
2525

2626
#import "MenuAccessibility.h"
27+
#import "ThreadUtilities.h"
28+
#import "sun_lwawt_macosx_CAccessibility.h"
29+
30+
static jclass sjc_CAccessibility = NULL;
2731

2832
/*
2933
* Implementing a protocol that represents menus both as submenu and as a
@@ -51,4 +55,31 @@ - (id _Nullable)accessibilityValue
5155
return NULL;
5256
}
5357

58+
/*
59+
* Return all non-ignored children.
60+
*/
61+
- (NSArray *)accessibilityChildren {
62+
JNIEnv *env = [ThreadUtilities getJNIEnv];
63+
GET_CACCESSIBILITY_CLASS_RETURN(nil);
64+
DECLARE_STATIC_METHOD_RETURN(sjm_getCurrentAccessiblePopupMenu, sjc_CAccessibility,
65+
"getCurrentAccessiblePopupMenu",
66+
"(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/Accessible;", nil);
67+
jobject axComponent = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility,
68+
sjm_getCurrentAccessiblePopupMenu,
69+
fAccessible, fComponent);
70+
71+
CommonComponentAccessibility *currentElement = [CommonComponentAccessibility createWithAccessible:axComponent
72+
withEnv:env withView:self->fView isCurrent:YES];
73+
74+
NSArray *children = [CommonComponentAccessibility childrenOfParent:currentElement
75+
withEnv:env
76+
withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN
77+
allowIgnored:NO];
78+
79+
if ([children count] == 0) {
80+
return nil;
81+
} else {
82+
return children;
83+
}
84+
}
5485
@end
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.event.MouseAdapter;
25+
import java.awt.event.MouseEvent;
26+
27+
import javax.swing.JFrame;
28+
import javax.swing.JMenu;
29+
import javax.swing.JMenuItem;
30+
import javax.swing.JPopupMenu;
31+
32+
/*
33+
* @test
34+
* @bug 8341311
35+
* @summary Verifies that VoiceOver announces correct number of child for PopupMenu on macOS
36+
* @requires os.family == "mac"
37+
* @library /java/awt/regtesthelpers
38+
* @build PassFailJFrame
39+
* @run main/manual TestPopupMenuChildCount
40+
*/
41+
42+
public class TestPopupMenuChildCount {
43+
public static void main(String[] args) throws Exception {
44+
String INSTRUCTIONS = """
45+
This test is applicable only on macOS.
46+
47+
Test UI contains an empty JFrame. On press of left/right mouse button,
48+
a PopupMenu will be visible.
49+
50+
Follow these steps to test the behaviour:
51+
52+
1. Start the VoiceOver (Press Command + F5) application
53+
2. Press Left/Right mouse button inside test frame window to open
54+
the PopupMenu
55+
3. VO should announce "Menu" with number of child items of the Popupmenu
56+
4. Press Up/Down arrow to traverse popupmenu child items
57+
5. Press Right arrow key to open submenu
58+
6. VO should announce "Menu" with correct number of child items
59+
for the submenu (For e.g. When Submenu-1 is open, VO should announce
60+
"Menu 4 items")
61+
7. Repeat the process for other submenus
62+
8. Press Pass if you are able to hear correct announcements
63+
else Fail""";
64+
65+
PassFailJFrame.builder()
66+
.instructions(INSTRUCTIONS)
67+
.columns(45)
68+
.testUI(TestPopupMenuChildCount::createUI)
69+
.build()
70+
.awaitAndCheck();
71+
}
72+
73+
private static JFrame createUI() {
74+
JFrame frame = new JFrame("Test Frame");
75+
76+
JPopupMenu popupmenu = new JPopupMenu();
77+
JMenuItem mi1 = new JMenuItem("MenuItem-1");
78+
JMenuItem mi2 = new JMenuItem("MenuItem-2");
79+
JMenuItem mi3 = new JMenuItem("MenuItem-3");
80+
popupmenu.add(mi1);
81+
popupmenu.add(mi2);
82+
popupmenu.add(mi3);
83+
84+
JMenu submenu1 = new JMenu("Submenu-1");
85+
submenu1.add("subOne");
86+
submenu1.add("subTwo");
87+
submenu1.add("subThree");
88+
89+
JMenu submenu2 = new JMenu("Submenu-2");
90+
submenu2.add("subOne");
91+
submenu2.add("subTwo");
92+
93+
JMenu submenu3 = new JMenu ("Submenu-3");
94+
submenu3.add("subOne");
95+
submenu1.add(submenu3);
96+
97+
popupmenu.add(submenu1);
98+
popupmenu.add(submenu2);
99+
100+
frame.addMouseListener(new MouseAdapter() {
101+
@Override
102+
public void mouseClicked(MouseEvent e) {
103+
popupmenu.show(e.getComponent(), e.getX(), e.getY());
104+
}
105+
});
106+
frame.setSize(300, 300);
107+
return frame;
108+
}
109+
}

0 commit comments

Comments
 (0)