6714324: Removing a component from a JTabbedPane does not clear its accessibleParent
authorkaddepalli
Mon, 17 Dec 2018 14:19:06 +0530
changeset 53182 6cf5fddfb93d
parent 53181 0434a6393b65
child 53183 eeac4e2558d7
6714324: Removing a component from a JTabbedPane does not clear its accessibleParent Reviewed-by: serb, sveerabhadra
src/java.desktop/share/classes/javax/swing/JTabbedPane.java
test/jdk/javax/accessibility/6714324/TabbedPaneMemLeak.java
--- a/src/java.desktop/share/classes/javax/swing/JTabbedPane.java	Sat Dec 15 10:35:45 2018 -0800
+++ b/src/java.desktop/share/classes/javax/swing/JTabbedPane.java	Mon Dec 17 14:19:06 2018 +0530
@@ -24,15 +24,39 @@
  */
 package javax.swing;
 
-import java.awt.*;
-import java.awt.event.*;
+import java.awt.Component;
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Cursor;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.FocusListener;
+
 import java.beans.JavaBean;
 import java.beans.BeanProperty;
 import java.beans.Transient;
-import java.util.*;
-import javax.swing.event.*;
-import javax.swing.plaf.*;
-import javax.accessibility.*;
+
+import java.util.Locale;
+import java.util.ArrayList;
+
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ChangeEvent;
+
+import javax.swing.plaf.TabbedPaneUI;
+import javax.swing.plaf.UIResource;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleIcon;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
 
 import sun.swing.SwingUtilities2;
 
@@ -923,6 +947,13 @@
         }
     }
 
+    private void clearAccessibleParent(Component c) {
+        AccessibleContext ac = c.getAccessibleContext();
+        if (ac != null) {
+            ac.setAccessibleParent(null);
+        }
+    }
+
     /**
      * Removes the tab at <code>index</code>.
      * After the component associated with <code>index</code> is removed,
@@ -1005,6 +1036,7 @@
                 if (components[i] == component) {
                     super.remove(i);
                     component.setVisible(true);
+                    clearAccessibleParent(component);
                     break;
                 }
             }
@@ -1552,6 +1584,7 @@
                     for (int i = 0; i < count; i++) {
                         if (children[i] == page.component) {
                             super.remove(i);
+                            clearAccessibleParent(children[i]);
                         }
                     }
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/accessibility/6714324/TabbedPaneMemLeak.java	Mon Dec 17 14:19:06 2018 +0530
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @key headful
+ * @bug 6714324
+ * @summary tests if removing a Tab from JTabbedComponent, clears the reference
+ * to the Page (AccessibleContext) object.
+ * @run main TabbedPaneMemLeak
+ */
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.JTabbedPane;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
+
+import java.awt.Component;
+import java.lang.reflect.Field;
+import java.util.Hashtable;
+
+public class TabbedPaneMemLeak
+{
+    private static void checkAccessibleParent(Component component) {
+        //Use reflection to check the value of accessibleContext, since directly calling getAccessibleContext()
+        //creates one, if not already present.
+        try {
+            Field field =
+                    component.getClass().getSuperclass().getSuperclass().getSuperclass().getSuperclass().getDeclaredField(
+                            "accessibleContext");
+            field.setAccessible(true);
+            AccessibleContext ctx = (AccessibleContext)field.get(component);
+            if (ctx != null) {
+                Field accessibleParentField = field.getType().getDeclaredField("accessibleParent");
+                accessibleParentField.setAccessible(true);
+                Accessible parent = (Accessible)accessibleParentField.get(ctx);
+                if (parent != null) {
+                    throw new RuntimeException("Test failed: AccessibleContext added on the wrong codepath.");
+                }
+            }
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new RuntimeException("Test failed: Unable to fetch AccessibleContext");
+        }
+
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        SwingUtilities.invokeAndWait(() -> {
+            JTabbedPane tabbedPane = new JTabbedPane();
+            if (tabbedPane.getAccessibleContext() != null) {  // Ensure that the JTabbedPane has an AccessibleContext
+                JComponent component = new JPanel();
+                System.out.println(component.getAccessibleContext().getAccessibleParent()); // null
+                tabbedPane.addTab("Component", component);
+                System.out.println(component.getAccessibleContext().getAccessibleParent()); // JTabbedPane$Page
+
+                JComponent component1 = new JPanel();
+                JComponent component2 = new JPanel();
+
+                tabbedPane.addTab("Component1", component1);
+                tabbedPane.setComponentAt(1, component2);
+
+                if (component1.getAccessibleContext().getAccessibleParent() != null) {
+                    throw new RuntimeException("Test failed: Parent AccessibleContext not cleared from the child component");
+                }
+
+                tabbedPane.removeAll(); // Could also be tabbedPane.remove(component) or tabbedPane.removeTabAt(0)
+                if (component.getAccessibleContext().getAccessibleParent() != null) {
+                    throw new RuntimeException("Test failed: Parent AccessibleContext not cleared from the child " +
+                            "component");
+                }
+
+                JSlider slider = new JSlider(0, 10);
+                Hashtable<Integer, JComponent> labels = slider.createStandardLabels(5, 2);
+
+                JComponent labelComp = labels.get(labels.keys().nextElement());
+
+                tabbedPane.add(labelComp);
+
+                checkAccessibleParent(labelComp);
+
+                tabbedPane.remove(labelComp);
+
+                checkAccessibleParent(labelComp);
+            }
+        });
+    }
+}