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 
  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 }