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