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