8240633: Memory leaks in the implementations of FileChooserUI
Reviewed-by: pbansal, psadhukhan
This commit is contained in:
parent
4f1fabd8d0
commit
b4863f9dd8
src/java.desktop/macosx/classes/com/apple/laf
test/jdk/javax/swing/JFileChooser
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2020, 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
|
||||
@ -25,20 +25,55 @@
|
||||
|
||||
package com.apple.laf;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.*;
|
||||
import javax.accessibility.Accessible;
|
||||
import javax.accessibility.AccessibleContext;
|
||||
import javax.accessibility.AccessibleState;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.ComboBoxEditor;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.plaf.*;
|
||||
import javax.swing.plaf.basic.*;
|
||||
import com.apple.laf.ClientPropertyApplicator.Property;
|
||||
import apple.laf.JRSUIConstants.Size;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.plaf.ActionMapUIResource;
|
||||
import javax.swing.plaf.ComboBoxUI;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.ListUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicComboBoxEditor;
|
||||
import javax.swing.plaf.basic.BasicComboBoxUI;
|
||||
import javax.swing.plaf.basic.ComboPopup;
|
||||
|
||||
import apple.laf.JRSUIConstants.Size;
|
||||
import com.apple.laf.AquaUtilControlSize.Sizeable;
|
||||
import com.apple.laf.AquaUtils.RecyclableSingleton;
|
||||
import com.apple.laf.ClientPropertyApplicator.Property;
|
||||
|
||||
// Inspired by MetalComboBoxUI, which also has a combined text-and-arrow button for noneditables
|
||||
public class AquaComboBoxUI extends BasicComboBoxUI implements Sizeable {
|
||||
@ -86,6 +121,11 @@ public class AquaComboBoxUI extends BasicComboBoxUI implements Sizeable {
|
||||
|
||||
protected void uninstallComponents() {
|
||||
getApplicator().removeFrom(comboBox);
|
||||
// AquaButtonUI install some listeners to all parents, which means that
|
||||
// we need to uninstall UI here to remove those listeners, because after
|
||||
// we remove them from ComboBox we lost the latest reference to them,
|
||||
// and our standard uninstallUI machinery will not call them.
|
||||
arrowButton.getUI().uninstallUI(arrowButton);
|
||||
super.uninstallComponents();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2020, 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
|
||||
@ -25,22 +25,90 @@
|
||||
|
||||
package com.apple.laf;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.dnd.*;
|
||||
import java.awt.event.*;
|
||||
import java.beans.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTarget;
|
||||
import java.awt.dnd.DropTargetAdapter;
|
||||
import java.awt.dnd.DropTargetDragEvent;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.text.DateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.DefaultListSelectionModel;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.filechooser.*;
|
||||
import javax.swing.plaf.*;
|
||||
import javax.swing.table.*;
|
||||
import javax.swing.event.AncestorEvent;
|
||||
import javax.swing.event.AncestorListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
import javax.swing.filechooser.FileView;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.FileChooserUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import sun.swing.SwingUtilities2;
|
||||
|
||||
@ -168,7 +236,6 @@ public class AquaFileChooserUI extends FileChooserUI {
|
||||
if (propertyChangeListener != null) {
|
||||
fc.addPropertyChangeListener(propertyChangeListener);
|
||||
}
|
||||
if (model != null) fc.addPropertyChangeListener(model);
|
||||
|
||||
ancestorListener = new AncestorListener(){
|
||||
public void ancestorAdded(final AncestorEvent e) {
|
||||
@ -196,6 +263,7 @@ public class AquaFileChooserUI extends FileChooserUI {
|
||||
fc.removePropertyChangeListener(propertyChangeListener);
|
||||
}
|
||||
fFileList.removeMouseListener(doubleClickListener);
|
||||
fc.removePropertyChangeListener(filterComboBoxModel);
|
||||
fc.removePropertyChangeListener(model);
|
||||
fc.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
|
||||
fc.removeAncestorListener(ancestorListener);
|
||||
@ -1741,7 +1809,17 @@ public class AquaFileChooserUI extends FileChooserUI {
|
||||
return fButtonActions[which];
|
||||
}
|
||||
|
||||
public void uninstallComponents(final JFileChooser fc) { //$ Metal (on which this is based) doesn't uninstall its components.
|
||||
public void uninstallComponents(final JFileChooser fc) {
|
||||
// AquaButtonUI install some listeners to all parents, which means that
|
||||
// we need to uninstall UI here to remove those listeners, because after
|
||||
// we remove them from FileChooser we lost the latest reference to them,
|
||||
// and our standard uninstallUI machinery will not call them.
|
||||
fApproveButton.getUI().uninstallUI(fApproveButton);
|
||||
fOpenButton.getUI().uninstallUI(fOpenButton);
|
||||
fNewFolderButton.getUI().uninstallUI(fNewFolderButton);
|
||||
fCancelButton.getUI().uninstallUI(fCancelButton);
|
||||
directoryComboBox.getUI().uninstallUI(directoryComboBox);
|
||||
filterComboBox.getUI().uninstallUI(filterComboBox);
|
||||
}
|
||||
|
||||
// Consistent with the AppKit NSSavePanel, clicks on a file (not a directory) should populate the text field
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UIManager.LookAndFeelInfo;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8240633
|
||||
* @key headful
|
||||
* @summary FileChooserUI delegates should clean listeners in JFileChooser
|
||||
*/
|
||||
public final class FileChooserListenerLeak {
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
EventQueue.invokeAndWait(()->{
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
checkListenersCount(chooser);
|
||||
LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
for (LookAndFeelInfo installedLookAndFeel : infos) {
|
||||
setLookAndFeel(installedLookAndFeel);
|
||||
SwingUtilities.updateComponentTreeUI(chooser);
|
||||
}
|
||||
}
|
||||
checkListenersCount(chooser);
|
||||
});
|
||||
}
|
||||
|
||||
private static void checkListenersCount(JFileChooser chooser) {
|
||||
test(chooser.getComponentListeners());
|
||||
test(chooser.getFocusListeners());
|
||||
test(chooser.getHierarchyListeners());
|
||||
test(chooser.getHierarchyBoundsListeners());
|
||||
test(chooser.getKeyListeners());
|
||||
test(chooser.getMouseListeners());
|
||||
test(chooser.getMouseMotionListeners());
|
||||
test(chooser.getMouseWheelListeners());
|
||||
test(chooser.getInputMethodListeners());
|
||||
test(chooser.getPropertyChangeListeners());
|
||||
test(chooser.getAncestorListeners());
|
||||
test(chooser.getVetoableChangeListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the count of specific listeners, assumes that the proper
|
||||
* implementation does not use more than 10 listeners.
|
||||
*/
|
||||
private static void test(Object[] listeners) {
|
||||
int length = listeners.length;
|
||||
if (length > 10) {
|
||||
throw new RuntimeException("The count of listeners is: " + length);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(laf.getClassName());
|
||||
} catch (UnsupportedLookAndFeelException ignored){
|
||||
System.out.println("Unsupported LookAndFeel: " + laf.getClassName());
|
||||
} catch (ClassNotFoundException | InstantiationException |
|
||||
IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user