8134116: Add more comprehensive fix and regression test for JDK-8133897

Use getTitleAt instead of Page.title field; add regression test

Reviewed-by: alexsch, serb
diff --git a/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java b/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java
index d50e660..de917a3 100644
--- a/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JTabbedPane.java
@@ -2095,9 +2095,10 @@
          */
         void setDisplayedMnemonicIndex(int mnemonicIndex) {
             if (this.mnemonicIndex != mnemonicIndex) {
-                if (mnemonicIndex != -1 && (title == null ||
+                String t = getTitle();
+                if (mnemonicIndex != -1 && (t == null ||
                         mnemonicIndex < 0 ||
-                        mnemonicIndex >= title.length())) {
+                        mnemonicIndex >= t.length())) {
                     throw new IllegalArgumentException(
                                 "Invalid mnemonic index: " + mnemonicIndex);
                 }
@@ -2116,7 +2117,7 @@
 
         void updateDisplayedMnemonicIndex() {
             setDisplayedMnemonicIndex(
-                SwingUtilities.findDisplayedMnemonicIndex(title, mnemonic));
+                SwingUtilities.findDisplayedMnemonicIndex(getTitle(), mnemonic));
         }
 
         /////////////////
@@ -2133,10 +2134,9 @@
         public String getAccessibleName() {
             if (accessibleName != null) {
                 return accessibleName;
-            } else if (title != null) {
-                return title;
+            } else {
+                return getTitle();
             }
-            return null;
         }
 
         public String getAccessibleDescription() {
@@ -2156,7 +2156,7 @@
             AccessibleStateSet states;
             states = parent.getAccessibleContext().getAccessibleStateSet();
             states.add(AccessibleState.SELECTABLE);
-            int i = parent.indexOfTab(title);
+            int i = parent.indexOfTabComponent(tabComponent);
             if (i == parent.getSelectedIndex()) {
                 states.add(AccessibleState.SELECTED);
             }
@@ -2164,7 +2164,7 @@
         }
 
         public int getAccessibleIndexInParent() {
-            return parent.indexOfTab(title);
+            return parent.indexOfTabComponent(tabComponent);
         }
 
         public int getAccessibleChildrenCount() {
@@ -2272,10 +2272,8 @@
         }
 
         public Rectangle getBounds() {
-            int i = parent.indexOfTab(title);
-            // Check for no title. Even though that's a bug in the app we should
-            // inhibit an ArrayIndexOutOfBoundsException from getTabBounds.
-            return (i == -1) ? null : parent.getUI().getTabBounds(parent, i);
+            return parent.getUI().
+                getTabBounds(parent, parent.indexOfTabComponent(tabComponent));
         }
 
         public void setBounds(Rectangle r) {
@@ -2343,6 +2341,11 @@
                 return null;
             }
         }
+
+        private String getTitle() {
+            return getTitleAt(parent.indexOfComponent(component));
+        }
+
     }
 
     /**
diff --git a/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java b/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java
new file mode 100644
index 0000000..15aa9f4
--- /dev/null
+++ b/jdk/test/javax/swing/JTabbedPane/8134116/Bug8134116.java
@@ -0,0 +1,130 @@
+
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.*;
+import javax.swing.plaf.nimbus.NimbusLookAndFeel;
+
+/*
+ * @test
+ * @bug 8134116
+ * @summary JTabbedPane$Page.getBounds throws IndexOutOfBoundsException
+ * @run main Bug8134116
+ */
+public class Bug8134116 {
+
+    public static void main(String args[]) throws Exception {
+
+        try {
+            UIManager.setLookAndFeel(new NimbusLookAndFeel());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        SwingUtilities.invokeAndWait(() -> {
+            JPanel panel0 = new JPanel();
+            BadPane badPane = new BadPane();
+            badPane.add("zero", panel0);
+            badPane.add("one", null);
+            JFrame frame = new JFrame();
+            frame.add(badPane);
+            frame.setSize(300, 300);
+            frame.setVisible(true);
+
+            AccessibleContext ac = badPane.getAccessibleContext();
+            Accessible page0 = ac.getAccessibleChild(0);
+            if (page0 == null) {
+                // Not something being tested, but checking anyway
+                throw new RuntimeException("getAccessibleChild(0) is null");
+            }
+            Accessible page1 = ac.getAccessibleChild(1);
+            if (page1 == null) {
+                // Not something being tested, but checking anyway
+                throw new RuntimeException("getAccessibleChild(1) is null");
+            }
+            // page0 and page1 are a JTabbedPane.Page, a private inner class
+            // and is an AccessibleContext
+            // and implements Accessible and AccessibleComponent
+            AccessibleContext pac0 = page0.getAccessibleContext();
+            AccessibleContext pac1 = page1.getAccessibleContext();
+
+            // the following would fail if JDK-8134116 fix not present
+
+            // test Page.getBounds
+            // ensure no IndexOutOfBoundsException
+            pac0.getAccessibleComponent().getBounds();
+
+            // test Page.getAccessibleStateSet
+            // At this point page 0 is selected
+            AccessibleStateSet accSS0 = pac0.getAccessibleStateSet();
+            if (!accSS0.contains(AccessibleState.SELECTED)) {
+                String msg = "Empty title -> AccessibleState.SELECTED not set";
+                throw new RuntimeException(msg);
+            }
+
+            // test Page.getAccessibleIndexInParent
+            if (pac0.getAccessibleIndexInParent() == -1) {
+                String msg = "Empty title -> negative AccessibleIndexInParent";
+                throw new RuntimeException(msg);
+            }
+
+            // test Page.getAccessibleName
+            String accName = pac0.getAccessibleName();
+            if (!accName.equals("zero")) {
+                String msg = "Empty title -> empty AccessibleName";
+                throw new RuntimeException(msg);
+            }
+            // test Page.getAccessibleName when component is null
+            accName = pac1.getAccessibleName();
+            if (!accName.equals("one")) {
+                String msg = "AccessibleName of null panel not 'one'";
+                throw new RuntimeException(msg);
+            }
+
+            // test Page.setDisplayedMnemonicIndex
+            //  Empty title -> IllegalArgumnetException
+            badPane.setDisplayedMnemonicIndexAt(0, 1);
+
+            // test Page.updateDisplayedMnemonicIndex
+            badPane.setMnemonicAt(0, KeyEvent.VK_Z);
+            if (badPane.getDisplayedMnemonicIndexAt(0) == -1) {
+                String msg="Empty title -> getDisplayedMnemonicIndexAt failure";
+                throw new RuntimeException(msg);
+            }
+        });
+    }
+
+    // The following is likely what is being done in Burp Suite
+    // https://portswigger.net/burp/ which fails in the same way, i.e. the
+    // pages List in JTabbedPane is not being managed properly and thus
+    // Page.title is "" for each page.  The overridden insertTab manages titles
+    // in the subclass passing a "" title to the superclass JTabbedPane through
+    // its insertTab.  Later an overridden getTitleAt returns the titles as
+    // managed by the subclass.
+    static class BadPane extends JTabbedPane {
+        private List<String> titles;
+
+        BadPane() {
+            titles = new ArrayList<String>(1);
+        }
+
+        @Override
+        public void insertTab( String title, Icon icon, Component component,
+                               String tip, int index ) {
+            titles.add(index, title);
+            super.insertTab("", icon, component, tip, index);
+        }
+
+        @Override
+        public String getTitleAt(int i) {
+            return titles.get(i);
+        }
+    }
+
+}