1 
2 import java.awt.*;
3 import java.awt.event.KeyEvent;
4 import java.util.ArrayList;
5 import java.util.List;
6 import javax.accessibility.Accessible;
7 import javax.accessibility.AccessibleContext;
8 import javax.accessibility.AccessibleState;
9 import javax.accessibility.AccessibleStateSet;
10 import javax.swing.*;
11 import javax.swing.plaf.nimbus.NimbusLookAndFeel;
12 
13 /*
14  * @test
15  * @key headful
16  * @bug 8134116
17  * @summary JTabbedPane$Page.getBounds throws IndexOutOfBoundsException
18  * @run main Bug8134116
19  */
20 public class Bug8134116 {
21 
22     private static volatile Exception exception = null;
23     private static JFrame frame;
24 
main(String args[])25     public static void main(String args[]) throws Exception {
26 
27         try {
28             UIManager.setLookAndFeel(new NimbusLookAndFeel());
29         } catch (Exception e) {
30             throw new RuntimeException(e);
31         }
32 
33         try {
34             SwingUtilities.invokeAndWait(() -> {
35                 JPanel panel0 = new JPanel();
36                 JPanel panel2 = new JPanel();
37                 BadPane badPane = new BadPane();
38                 badPane.add("zero", panel0);
39                 badPane.add("one", null);  // no component
40                 badPane.add("", panel2);  // no title
41                 badPane.add("", null); // no component, no title
42                 // but give it that via a tabComponent
43                 JPanel tabComponent = new JPanel();
44                 JLabel tabComponentLabel = new JLabel("three");
45                 tabComponent.add(tabComponentLabel);
46                 badPane.setTabComponentAt(3, tabComponent);
47                 frame = new JFrame();
48                 frame.add(badPane);
49                 frame.setSize(300, 300);
50                 frame.setVisible(true);
51 
52                 try {
53                     AccessibleContext ac = badPane.getAccessibleContext();
54                     Accessible page0 = ac.getAccessibleChild(0);
55                     if (page0 == null) {
56                         // Not something being tested, but checking anyway
57                         throw new RuntimeException("getAccessibleChild(0) is null");
58                     }
59                     Accessible page1 = ac.getAccessibleChild(1);
60                     if (page1 == null) {
61                         // Not something being tested, but checking anyway
62                         throw new RuntimeException("getAccessibleChild(1) is null");
63                     }
64                     Accessible page2 = ac.getAccessibleChild(2);
65                     Accessible page3 = ac.getAccessibleChild(3);
66                     // page0 - page3 are JTabbedPane.Page, a private inner class
67                     // and is an AccessibleContext
68                     // and implements Accessible and AccessibleComponent
69                     AccessibleContext pac0 = page0.getAccessibleContext();
70                     AccessibleContext pac1 = page1.getAccessibleContext();
71                     AccessibleContext pac2 = page2.getAccessibleContext();
72                     AccessibleContext pac3 = page3.getAccessibleContext();
73 
74                     // test Page.getBounds
75                     // ensure no IndexOutOfBoundsException
76                     Rectangle r0 = pac0.getAccessibleComponent().getBounds();
77                     // make sure second Bounds is different than first
78                     Rectangle r1  = pac1.getAccessibleComponent().getBounds();
79                     if (r1.equals(r0)) {
80                         String msg = "Second tab should not have same bounds as first tab";
81                         throw new RuntimeException(msg);
82                     }
83 
84                     // test Page.getAccessibleStateSet
85                     // At this point page 0 is selected
86                     AccessibleStateSet accSS0 = pac0.getAccessibleStateSet();
87                     if (!accSS0.contains(AccessibleState.SELECTED)) {
88                         String msg = "Empty title -> AccessibleState.SELECTED not set";
89                         throw new RuntimeException(msg);
90                     }
91                     // select second tab
92                     badPane.setSelectedIndex(1);
93                     AccessibleStateSet accSS1 = pac1.getAccessibleStateSet();
94                     if (!accSS1.contains(AccessibleState.SELECTED)) {
95                         String msg = "Second tab selected but AccessibleState.SELECTED not set";
96                         throw new RuntimeException(msg);
97                     }
98                     // select third tab
99                     badPane.setSelectedIndex(2);
100                     AccessibleStateSet accSS2 = pac2.getAccessibleStateSet();
101                     if (!accSS1.contains(AccessibleState.SELECTED)) {
102                         String msg = "Third tab selected but AccessibleState.SELECTED not set";
103                         throw new RuntimeException(msg);
104                     }
105                     // select fourth tab
106                     badPane.setSelectedIndex(3);
107                     AccessibleStateSet accSS3 = pac3.getAccessibleStateSet();
108                     if (!accSS1.contains(AccessibleState.SELECTED)) {
109                         String msg = "Fourth tab selected but AccessibleState.SELECTED not set";
110                         throw new RuntimeException(msg);
111                     }
112 
113                     // test Page.getAccessibleIndexInParent
114                     if (pac0.getAccessibleIndexInParent() == -1) {
115                         String msg = "Empty title -> negative AccessibleIndexInParent";
116                         throw new RuntimeException(msg);
117                     }
118                     if (pac0.getAccessibleIndexInParent() != 0) {
119                         String msg = "first tab is not at index 0 in parent";
120                         throw new RuntimeException(msg);
121                     }
122                     if (pac1.getAccessibleIndexInParent() != 1) {
123                         String msg = "second tab (null component) is not at index 1 in parent";
124                         throw new RuntimeException(msg);
125                     }
126                     if (pac2.getAccessibleIndexInParent() != 2) {
127                         String msg = "third tab (empty title) string is not at index 2 in parent";
128                         throw new RuntimeException(msg);
129                     }
130                     if (pac3.getAccessibleIndexInParent() != 3) {
131                         String msg = "fourth tab (empty title, null component, has tabComponent) string is not at index 3 in parent";
132                         throw new RuntimeException(msg);
133                     }
134 
135                     // test Page.getAccessibleName
136                     String accName = pac0.getAccessibleName();
137                     if (!accName.equals("zero")) {
138                         String msg = "Empty title -> empty AccessibleName";
139                         throw new RuntimeException(msg);
140                     }
141                     // test Page.getAccessibleName when component is null
142                     accName = pac1.getAccessibleName();
143                     if (!accName.equals("one")) {
144                         String msg = "AccessibleName of null panel not 'one'";
145                         throw new RuntimeException(msg);
146                     }
147 
148                     // test Page.setDisplayedMnemonicIndex
149                     //  Empty title -> IllegalArgumnetException
150                     badPane.setDisplayedMnemonicIndexAt(0, 1);
151 
152                     // test Page.updateDisplayedMnemonicIndex
153                     badPane.setMnemonicAt(0, KeyEvent.VK_Z);
154                     if (badPane.getDisplayedMnemonicIndexAt(0) == -1) {
155                         String msg="Empty title -> getDisplayedMnemonicIndexAt failure";
156                         throw new RuntimeException(msg);
157                     }
158                 } catch (Exception e) {
159                     exception = e;
160                 }
161             });
162             if (exception != null) {
163                 System.out.println("Test failed: " + exception.getMessage());
164                 throw exception;
165             } else {
166                 System.out.println("Test passed.");
167             }
168         } finally {
169             if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose());
170         }
171     }
172 
173     // The following is likely what is being done in Burp Suite
174     // https://portswigger.net/burp/ which fails in the same way, i.e. the
175     // pages List in JTabbedPane is not being managed properly and thus
176     // Page.title is "" for each page.  The overridden insertTab manages titles
177     // in the subclass passing a "" title to the superclass JTabbedPane through
178     // its insertTab.  Later an overridden getTitleAt returns the titles as
179     // managed by the subclass.
180     static class BadPane extends JTabbedPane {
181         private List<String> titles;
182 
BadPane()183         BadPane() {
184             titles = new ArrayList<String>(1);
185         }
186 
187         @Override
insertTab( String title, Icon icon, Component component, String tip, int index )188         public void insertTab( String title, Icon icon, Component component,
189                                String tip, int index ) {
190             titles.add(index, title);
191             super.insertTab("", icon, component, tip, index);
192         }
193 
194         @Override
getTitleAt(int i)195         public String getTitleAt(int i) {
196             return titles.get(i);
197         }
198     }
199 
200 }
201