changeset 12765:4c078d8904c9

8134947: [macosx] Various memory leaks in Aqua look and feel Reviewed-by: azvegint, alexsch
author serb
date Mon, 07 Sep 2015 23:57:21 +0300
parents f7b346d83034
children 177450b9f1a6
files src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java src/java.desktop/macosx/classes/com/apple/laf/AquaEditorPaneUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaSpinnerUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaTextAreaUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaTextFieldUI.java src/java.desktop/macosx/classes/com/apple/laf/AquaTextPaneUI.java src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java test/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java
diffstat 10 files changed, 373 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -36,19 +36,27 @@
 import javax.swing.text.*;
 
 @SuppressWarnings("serial") // Superclass is not serializable across versions
-public class AquaCaret extends DefaultCaret implements UIResource, PropertyChangeListener {
-    final boolean isMultiLineEditor;
-    final JTextComponent c;
-
-    boolean mFocused = false;
+public class AquaCaret extends DefaultCaret
+        implements UIResource, PropertyChangeListener {
 
-    public AquaCaret(final Window inParentWindow, final JTextComponent inComponent) {
-        super();
-        c = inComponent;
-        isMultiLineEditor = (c instanceof JTextArea || c instanceof JEditorPane);
-        inComponent.addPropertyChangeListener(this);
+    private boolean isMultiLineEditor;
+    private boolean mFocused = false;
+    private boolean fPainting = false;
+
+    @Override
+    public void install(final JTextComponent c) {
+        super.install(c);
+        isMultiLineEditor = c instanceof JTextArea || c instanceof JEditorPane;
+        c.addPropertyChangeListener(this);
     }
 
+    @Override
+    public void deinstall(final JTextComponent c) {
+        c.removePropertyChangeListener(this);
+        super.deinstall(c);
+    }
+
+    @Override
     protected Highlighter.HighlightPainter getSelectionPainter() {
         return AquaHighlighter.getInstance();
     }
@@ -56,11 +64,13 @@
     /**
      * Only show the flashing caret if the selection range is zero
      */
+    @Override
     public void setVisible(boolean e) {
         if (e) e = getDot() == getMark();
         super.setVisible(e);
     }
 
+    @Override
     protected void fireStateChanged() {
         // If we have focus the caret should only flash if the range length is zero
         if (mFocused) setVisible(getComponent().isEditable());
@@ -68,6 +78,7 @@
         super.fireStateChanged();
     }
 
+    @Override
     public void propertyChange(final PropertyChangeEvent evt) {
         final String propertyName = evt.getPropertyName();
 
@@ -87,6 +98,7 @@
     // --- FocusListener methods --------------------------
 
     private boolean shouldSelectAllOnFocus = true;
+    @Override
     public void focusGained(final FocusEvent e) {
         final JTextComponent component = getComponent();
         if (!component.isEnabled() || !component.isEditable()) {
@@ -122,12 +134,13 @@
         super.focusGained(e);
     }
 
+    @Override
     public void focusLost(final FocusEvent e) {
         mFocused = false;
         shouldSelectAllOnFocus = true;
         if (isMultiLineEditor) {
             setVisible(false);
-            c.repaint();
+            getComponent().repaint();
         } else {
             super.focusLost(e);
         }
@@ -136,6 +149,7 @@
     // This fixes the problem where when on the mac you have to ctrl left click to
     // get popup triggers the caret has code that only looks at button number.
     // see radar # 3125390
+    @Override
     public void mousePressed(final MouseEvent e) {
         if (!e.isPopupTrigger()) {
             super.mousePressed(e);
@@ -153,6 +167,7 @@
      * @param r  the current location of the caret
      * @see #paint
      */
+    @Override
     protected synchronized void damage(final Rectangle r) {
         if (r == null || fPainting) return;
 
@@ -182,12 +197,12 @@
         repaint();
     }
 
-    boolean fPainting = false;
-
-    // See <rdar://problem/3833837> 1.4.2_05-141.3: JTextField performance with Aqua L&F
-    // We are getting into a circular condition with the BasicCaret paint code since it doesn't know about the fact that our
-    // damage routine above elminates the border. Sadly we can't easily change either one, so we will
-    // add a painting flag and not damage during a repaint.
+    // See <rdar://problem/3833837> 1.4.2_05-141.3: JTextField performance with
+    // Aqua L&F. We are getting into a circular condition with the BasicCaret
+    // paint code since it doesn't know about the fact that our damage routine
+    // above elminates the border. Sadly we can't easily change either one, so
+    // we will add a painting flag and not damage during a repaint.
+    @Override
     public void paint(final Graphics g) {
         if (isVisible()) {
             fPainting = true;
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaEditorPaneUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaEditorPaneUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -39,6 +39,7 @@
     }
 
     boolean oldDragState = false;
+    @Override
     protected void installDefaults(){
         super.installDefaults();
         if(!GraphicsEnvironment.isHeadless()){
@@ -47,6 +48,7 @@
         }
     }
 
+    @Override
     protected void uninstallDefaults(){
         if(!GraphicsEnvironment.isHeadless()){
             getComponent().setDragEnabled(oldDragState);
@@ -55,12 +57,14 @@
     }
 
     FocusListener focusListener;
+    @Override
     protected void installListeners(){
         super.installListeners();
         focusListener = createFocusListener();
         getComponent().addFocusListener(focusListener);
     }
 
+    @Override
     protected void installKeyboardActions() {
         super.installKeyboardActions();
         AquaKeyBindings bindings = AquaKeyBindings.instance();
@@ -69,6 +73,7 @@
         bindings.installAquaUpDownActions(c);
     }
 
+    @Override
     protected void uninstallListeners(){
         getComponent().removeFocusListener(focusListener);
         super.uninstallListeners();
@@ -78,12 +83,12 @@
         return new AquaFocusHandler();
     }
 
-    protected Caret createCaret(){
-        final Window owningWindow = SwingUtilities.getWindowAncestor(getComponent());
-        final AquaCaret returnValue = new AquaCaret(owningWindow, getComponent());
-        return returnValue;
+    @Override
+    protected Caret createCaret() {
+        return new AquaCaret();
     }
 
+    @Override
     protected Highlighter createHighlighter(){
         return new AquaHighlighter();
     }
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -66,6 +66,7 @@
     protected Color fNotSelectedTextColor;
 
     AquaInternalFrameBorder fAquaBorder;
+    private ResizeBox resizeBox;
 
     // for button tracking
     boolean fMouseOverPressedButton;
@@ -96,6 +97,7 @@
     }
 
     /// Inherit  (but be careful to check everything they call):
+    @Override
     public void installUI(final JComponent c) {
 //        super.installUI(c);  // Swing 1.1.1 has a bug in installUI - it doesn't check for null northPane
         frame = (JInternalFrame)c;
@@ -125,12 +127,14 @@
         c.setBorder(new CompoundUIBorder(fIsPallet ? paletteWindowShadow.get() : documentWindowShadow.get(), c.getBorder()));
     }
 
+    @Override
     protected void installDefaults() {
         super.installDefaults();
         fSelectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
         fNotSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
     }
 
+    @Override
     public void setSouthPane(final JComponent c) {
         if (southPane != null) {
             frame.remove(southPane);
@@ -144,6 +148,7 @@
     }
 
     static final RecyclableSingleton<Icon> closeIcon = new RecyclableSingleton<Icon>() {
+        @Override
         protected Icon getInstance() {
             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_CLOSE_BOX);
         }
@@ -153,6 +158,7 @@
     }
 
     static final RecyclableSingleton<Icon> minimizeIcon = new RecyclableSingleton<Icon>() {
+        @Override
         protected Icon getInstance() {
             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_COLLAPSE_BOX);
         }
@@ -162,6 +168,7 @@
     }
 
     static final RecyclableSingleton<Icon> zoomIcon = new RecyclableSingleton<Icon>() {
+        @Override
         protected Icon getInstance() {
             return new AquaInternalFrameButtonIcon(Widget.TITLE_BAR_ZOOM_BOX);
         }
@@ -175,6 +182,7 @@
             painter.state.set(widget);
         }
 
+        @Override
         public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
             painter.state.set(getStateFor(c));
             super.paintIcon(c, g, x, y);
@@ -184,28 +192,24 @@
             return State.ROLLOVER;
         }
 
+        @Override
         public int getIconWidth() {
             return 19;
         }
 
+        @Override
         public int getIconHeight() {
             return 19;
         }
     }
 
+    @Override
     protected void installKeyboardActions() {
     } //$ Not Mac-ish - should we support?
 
-    protected ResizeBox resizeBox;
+    @Override
     protected void installComponents() {
         final JLayeredPane layeredPane = frame.getLayeredPane();
-        if (resizeBox != null) {
-            resizeBox.removeListeners();
-            layeredPane.removeComponentListener(resizeBox);
-            layeredPane.remove(resizeBox);
-            resizeBox = null;
-        }
-
         resizeBox = new ResizeBox(layeredPane);
         resizeBox.repositionResizeBox();
 
@@ -218,6 +222,7 @@
     }
 
     /// Inherit all the listeners - that's the main reason we subclass Basic!
+    @Override
     protected void installListeners() {
         fPropertyListener = new PropertyListener();
         frame.addPropertyChangeListener(fPropertyListener);
@@ -225,22 +230,36 @@
     }
 
     // uninstallDefaults
-    // uninstallComponents
+
+    @Override
+    protected void uninstallComponents() {
+        super.uninstallComponents();
+        final JLayeredPane layeredPane = frame.getLayeredPane();
+        resizeBox.removeListeners();
+        layeredPane.removeComponentListener(resizeBox);
+        layeredPane.remove(resizeBox);
+        resizeBox = null;
+    }
+
+    @Override
     protected void uninstallListeners() {
         super.uninstallListeners();
         frame.removePropertyChangeListener(fPropertyListener);
     }
 
+    @Override
     protected void uninstallKeyboardActions() {
     }
 
     // Called when a DesktopIcon replaces an InternalFrame & vice versa
     //protected void replacePane(JComponent currentPane, JComponent newPane) {}
+    @Override
     protected void installMouseHandlers(final JComponent c) {
         c.addMouseListener(borderListener);
         c.addMouseMotionListener(borderListener);
     }
 
+    @Override
     protected void deinstallMouseHandlers(final JComponent c) {
         c.removeMouseListener(borderListener);
         c.removeMouseMotionListener(borderListener);
@@ -256,6 +275,7 @@
         return map;
     }
 
+    @Override
     public Dimension getPreferredSize(JComponent x) {
         Dimension preferredSize = super.getPreferredSize(x);
         Dimension minimumSize = frame.getMinimumSize();
@@ -268,6 +288,7 @@
         return preferredSize;
     }
 
+    @Override
     public void setNorthPane(final JComponent c) {
         replacePane(northPane, c);
         northPane = c;
@@ -278,6 +299,7 @@
      * and adds it to the frame.
      * Reverse process for the <code>currentPane</code>.
      */
+    @Override
     protected void replacePane(final JComponent currentPane, final JComponent newPane) {
         if (currentPane != null) {
             deinstallMouseHandlers(currentPane);
@@ -290,6 +312,7 @@
     }
 
     // Our "Border" listener is shared by the AquaDesktopIcon
+    @Override
     protected MouseInputAdapter createBorderListener(final JInternalFrame w) {
         return new AquaBorderListener();
     }
@@ -374,6 +397,7 @@
         protected final int RESIZE_NONE = 0;
         private boolean discardRelease = false;
 
+        @Override
         public void mouseClicked(final MouseEvent e) {
             if (didForwardEvent(e)) return;
 
@@ -406,6 +430,7 @@
             fAquaBorder.repaintButtonArea(frame);
         }
 
+        @Override
         public void mouseReleased(final MouseEvent e) {
             if (didForwardEvent(e)) return;
 
@@ -461,6 +486,7 @@
             resizeDir = RESIZE_NONE;
         }
 
+        @Override
         public void mousePressed(final MouseEvent e) {
             if (didForwardEvent(e)) return;
 
@@ -527,6 +553,7 @@
             return true;
         }
 
+        @Override
         public void mouseDragged(final MouseEvent e) {
 // do not forward drags
 //            if (didForwardEvent(e)) return;
@@ -576,6 +603,7 @@
             return;
         }
 
+        @Override
         public void mouseMoved(final MouseEvent e) {
             if (didForwardEvent(e)) return;
             updateRollover(e);
@@ -614,7 +642,11 @@
             if (hitComponent == null || hitComponent == frame) return false;
 
             final Point hitComponentPoint = SwingUtilities.convertPoint(pane, parentPoint, hitComponent);
-            hitComponent.dispatchEvent(new MouseEvent(hitComponent, e.getID(), e.getWhen(), e.getModifiers(), hitComponentPoint.x, hitComponentPoint.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()));
+            hitComponent.dispatchEvent(
+                    new MouseEvent(hitComponent, e.getID(), e.getWhen(),
+                                   e.getModifiers(), hitComponentPoint.x,
+                                   hitComponentPoint.y, e.getClickCount(),
+                                   e.isPopupTrigger(), e.getButton()));
             return true;
         }
 
@@ -668,6 +700,7 @@
     }
 
     class PropertyListener implements PropertyChangeListener {
+        @Override
         public void propertyChange(final PropertyChangeEvent e) {
             final String name = e.getPropertyName();
             if (FRAME_TYPE.equals(name)) {
@@ -704,14 +737,17 @@
     } // end class PaletteListener
 
     static final InternalFrameShadow documentWindowShadow = new InternalFrameShadow() {
+        @Override
         Border getForegroundShadowBorder() {
             return new AquaUtils.SlicedShadowBorder(new Painter() {
+                @Override
                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
                     g.setColor(new Color(0, 0, 0, 196));
                     g.fillRoundRect(x, y, w, h, 16, 16);
                     g.fillRect(x, y + h - 16, w, 16);
                 }
             }, new Painter() {
+                @Override
                 public void paint(final Graphics g, int x, int y, int w, int h) {
                     g.setColor(new Color(0, 0, 0, 64));
                     g.drawLine(x + 2, y - 8, x + w - 2, y - 8);
@@ -720,14 +756,17 @@
             0, 7, 1.1f, 1.0f, 24, 51, 51, 25, 25, 25, 25);
         }
 
+        @Override
         Border getBackgroundShadowBorder() {
             return new AquaUtils.SlicedShadowBorder(new Painter() {
+                @Override
                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
                     g.setColor(new Color(0, 0, 0, 128));
                     g.fillRoundRect(x - 3, y - 8, w + 6, h, 16, 16);
                     g.fillRect(x - 3, y + h - 20, w + 6, 19);
                 }
             }, new Painter() {
+                @Override
                 public void paint(final Graphics g, int x, int y, int w, int h) {
                     g.setColor(new Color(0, 0, 0, 32));
                     g.drawLine(x, y - 11, x + w - 1, y - 11);
@@ -738,8 +777,10 @@
     };
 
     static final InternalFrameShadow paletteWindowShadow = new InternalFrameShadow() {
+        @Override
         Border getForegroundShadowBorder() {
             return new AquaUtils.SlicedShadowBorder(new Painter() {
+                @Override
                 public void paint(final Graphics g, final int x, final int y, final int w, final int h) {
                     g.setColor(new Color(0, 0, 0, 128));
                     g.fillRect(x, y + 3, w, h - 3);
@@ -748,6 +789,7 @@
             0, 3, 1.0f, 1.0f, 10, 25, 25, 12, 12, 12, 12);
         }
 
+        @Override
         Border getBackgroundShadowBorder() {
             return getForegroundShadowBorder();
         }
@@ -762,19 +804,23 @@
         abstract Border getForegroundShadowBorder();
         abstract Border getBackgroundShadowBorder();
 
+        @Override
         protected Border getInstance() {
             final Border fgShadow = getForegroundShadowBorder();
             final Border bgShadow = getBackgroundShadowBorder();
 
             return new Border() {
+                @Override
                 public Insets getBorderInsets(final Component c) {
                     return fgShadow.getBorderInsets(c);
                 }
 
+                @Override
                 public boolean isBorderOpaque() {
                     return false;
                 }
 
+                @Override
                 public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) {
                     if (((JInternalFrame)c).isSelected()) {
                         fgShadow.paintBorder(c, g, x, y, w, h);
@@ -790,6 +836,7 @@
         @Override
         protected Icon getInstance() {
             return new AquaIcon.ScalingJRSUIIcon(11, 11) {
+                @Override
                 public void initIconPainter(final AquaPainter<JRSUIState> iconState) {
                     iconState.state.set(Widget.GROW_BOX_TEXTURED);
                     iconState.state.set(WindowType.UTILITY);
@@ -799,12 +846,15 @@
     };
 
     @SuppressWarnings("serial") // Superclass is not serializable across versions
-    class ResizeBox extends JLabel implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener, PropertyChangeListener, UIResource {
-        final JLayeredPane layeredPane;
-        Dimension originalSize;
-        Point originalLocation;
+    private final class ResizeBox extends JLabel
+            implements MouseListener, MouseMotionListener, MouseWheelListener,
+            ComponentListener, PropertyChangeListener, UIResource {
 
-        public ResizeBox(final JLayeredPane layeredPane) {
+        private final JLayeredPane layeredPane;
+        private Dimension originalSize;
+        private Point originalLocation;
+
+        ResizeBox(final JLayeredPane layeredPane) {
             super(RESIZE_ICON.get());
             setSize(11, 11);
             this.layeredPane = layeredPane;
@@ -895,14 +945,18 @@
             return c;
         }
 
+        @Override
         public void mouseClicked(final MouseEvent e) {
             forwardEventToFrame(e);
         }
 
+        @Override
         public void mouseEntered(final MouseEvent e) { }
 
+        @Override
         public void mouseExited(final MouseEvent e) { }
 
+        @Override
         public void mousePressed(final MouseEvent e) {
             if (frame == null) return;
 
@@ -916,6 +970,7 @@
             forwardEventToFrame(e);
         }
 
+        @Override
         public void mouseReleased(final MouseEvent e) {
             if (originalLocation != null) {
                 resizeInternalFrame(e.getPoint());
@@ -927,13 +982,16 @@
             forwardEventToFrame(e);
         }
 
+        @Override
         public void mouseDragged(final MouseEvent e) {
             resizeInternalFrame(e.getPoint());
             repositionResizeBox();
         }
 
+        @Override
         public void mouseMoved(final MouseEvent e) { }
 
+        @Override
         public void mouseWheelMoved(final MouseWheelEvent e) {
             final Point pt = new Point();
             final Component c = getComponentToForwardTo(e, pt);
@@ -945,20 +1003,25 @@
                     e.getPreciseWheelRotation()));
         }
 
+        @Override
         public void componentResized(final ComponentEvent e) {
             repositionResizeBox();
         }
 
+        @Override
         public void componentShown(final ComponentEvent e) {
             repositionResizeBox();
         }
 
+        @Override
         public void componentMoved(final ComponentEvent e) {
             repositionResizeBox();
         }
 
+        @Override
         public void componentHidden(final ComponentEvent e) { }
 
+        @Override
         public void propertyChange(final PropertyChangeEvent evt) {
             if (!"resizable".equals(evt.getPropertyName())) return;
             setVisible(Boolean.TRUE.equals(evt.getNewValue()));
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaSpinnerUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaSpinnerUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -49,12 +49,14 @@
  * so we can't subclass!
  */
 public class AquaSpinnerUI extends SpinnerUI {
-    private static final RecyclableSingleton<? extends PropertyChangeListener> propertyChangeListener = new RecyclableSingletonFromDefaultConstructor<PropertyChangeHandler>(PropertyChangeHandler.class);
+    private static final RecyclableSingleton<? extends PropertyChangeListener> propertyChangeListener
+            = new RecyclableSingletonFromDefaultConstructor<>(PropertyChangeHandler.class);
     static PropertyChangeListener getPropertyChangeListener() {
         return propertyChangeListener.get();
     }
 
-    private static final RecyclableSingleton<ArrowButtonHandler> nextButtonHandler = new RecyclableSingleton<ArrowButtonHandler>() {
+    private static final RecyclableSingleton<ArrowButtonHandler> nextButtonHandler
+            = new RecyclableSingleton<ArrowButtonHandler>() {
         @Override
         protected ArrowButtonHandler getInstance() {
             return new ArrowButtonHandler("increment", true);
@@ -63,7 +65,8 @@
     static ArrowButtonHandler getNextButtonHandler() {
         return nextButtonHandler.get();
     }
-    private static final RecyclableSingleton<ArrowButtonHandler> previousButtonHandler = new RecyclableSingleton<ArrowButtonHandler>() {
+    private static final RecyclableSingleton<ArrowButtonHandler> previousButtonHandler
+            = new RecyclableSingleton<ArrowButtonHandler>() {
         @Override
         protected ArrowButtonHandler getInstance() {
             return new ArrowButtonHandler("decrement", false);
@@ -73,8 +76,10 @@
         return previousButtonHandler.get();
     }
 
-    JSpinner spinner;
-    SpinPainter spinPainter;
+    private JSpinner spinner;
+    private SpinPainter spinPainter;
+    private TransparentButton next;
+    private TransparentButton prev;
 
     public static ComponentUI createUI(final JComponent c) {
         return new AquaSpinnerUI();
@@ -87,12 +92,13 @@
     }
 
     boolean wasOpaque;
+    @Override
     public void installUI(final JComponent c) {
         this.spinner = (JSpinner)c;
         installDefaults();
         installListeners();
-        final TransparentButton next = createNextButton();
-        final TransparentButton prev = createPreviousButton();
+        next = createNextButton();
+        prev = createPreviousButton();
         spinPainter = new SpinPainter(next, prev);
 
         maybeAdd(next, "Next");
@@ -111,11 +117,21 @@
         spinner.setOpaque(false);
     }
 
+    @Override
     public void uninstallUI(final JComponent c) {
         uninstallDefaults();
         uninstallListeners();
         spinner.setOpaque(wasOpaque);
+        spinPainter = null;
         spinner = null;
+        // 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 spinner we lost the latest reference to them,
+        // and our standard uninstallUI machinery will not call them.
+        next.getUI().uninstallUI(next);
+        prev.getUI().uninstallUI(prev);
+        next = null;
+        prev = null;
         c.removeAll();
     }
 
@@ -164,6 +180,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public int getBaseline(JComponent c, int width, int height) {
         super.getBaseline(c, width, height);
         JComponent editor = spinner.getEditor();
@@ -182,6 +199,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
             JComponent c) {
         super.getBaselineResizeBehavior(c);
@@ -200,8 +218,10 @@
             interceptRepaints = true;
         }
 
+        @Override
         public void paint(final Graphics g) {}
 
+        @Override
         public void repaint() {
             // only intercept repaints if we are after this has been initialized
             // otherwise we can't talk to our containing class
@@ -315,6 +335,7 @@
             return (src instanceof JSpinner) ? (JSpinner)src : null;
         }
 
+        @Override
         public void actionPerformed(final ActionEvent e) {
             if (!(e.getSource() instanceof javax.swing.Timer)) {
                 // Most likely resulting from being in ActionMap.
@@ -423,6 +444,7 @@
             return -1;
         }
 
+        @Override
         public void mousePressed(final MouseEvent e) {
             if (!SwingUtilities.isLeftMouseButton(e) || !e.getComponent().isEnabled()) return;
             spinner = eventToSpinner(e);
@@ -431,13 +453,17 @@
             focusSpinnerIfNecessary();
         }
 
+        @Override
         public void mouseReleased(final MouseEvent e) {
             autoRepeatTimer.stop();
             spinner = null;
         }
 
+        @Override
         public void mouseClicked(final MouseEvent e) {}
+        @Override
         public void mouseEntered(final MouseEvent e) {}
+        @Override
         public void mouseExited(final MouseEvent e) {}
 
         /**
@@ -485,6 +511,7 @@
             }
         }
 
+        @Override
         public void paint(final Graphics g) {
             if (spinner.isOpaque()) {
                 g.setColor(spinner.getBackground());
@@ -511,6 +538,7 @@
             painter.paint(g, spinner, 0, 0, bounds.width, bounds.height);
         }
 
+        @Override
         public Dimension getPreferredSize() {
             final Size size = AquaUtilControlSize.getUserSizeFrom(this);
 
@@ -533,6 +561,7 @@
         private Component editor = null;
         private Component painter = null;
 
+        @Override
         public void addLayoutComponent(final String name, final Component c) {
             if ("Next".equals(name)) {
                 nextButton = c;
@@ -545,6 +574,7 @@
             }
         }
 
+        @Override
         public void removeLayoutComponent(Component c) {
             if (c == nextButton) {
                 c = null;
@@ -561,6 +591,7 @@
             return (c == null) ? new Dimension(0, 0) : c.getPreferredSize();
         }
 
+        @Override
         public Dimension preferredLayoutSize(final Container parent) {
 //            Dimension nextD = preferredSize(nextButton);
 //            Dimension previousD = preferredSize(previousButton);
@@ -579,6 +610,7 @@
             return size;
         }
 
+        @Override
         public Dimension minimumLayoutSize(final Container parent) {
             return preferredLayoutSize(parent);
         }
@@ -589,6 +621,7 @@
             }
         }
 
+        @Override
         public void layoutContainer(final Container parent) {
             final Insets insets = parent.getInsets();
             final int availWidth = parent.getWidth() - (insets.left + insets.right);
@@ -629,6 +662,7 @@
      * property changes are delegated to protected methods.
      */
     static class PropertyChangeHandler implements PropertyChangeListener {
+        @Override
         public void propertyChange(final PropertyChangeEvent e) {
             final String propertyName = e.getPropertyName();
             final JSpinner spinner = (JSpinner)(e.getSource());
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaTabbedPaneUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -95,6 +95,17 @@
         super.assureRectsCreated(tabCount);
     }
 
+    @Override
+    protected void uninstallListeners() {
+        // We're not just a mouseListener, we're a mouseMotionListener
+        if (mouseListener instanceof  MouseHandler) {
+            final MouseHandler mh = (MouseHandler) mouseListener;
+            mh.dispose();
+            tabPane.removeMouseMotionListener(mh);
+        }
+        super.uninstallListeners();
+    }
+
     protected void uninstallDefaults() {
         contentDrawingInsets.set(0, 0, 0, 0);
     }
@@ -409,7 +420,15 @@
         paintTabNormalFromRect(g, tabPlacement, rects[tabIndex], tabIndex, fIconRect, fTextRect, active, frameActive, isLeftToRight);
     }
 
-    protected void paintTabNormalFromRect(final Graphics g, final int tabPlacement, final Rectangle tabRect, final int nonRectIndex, final Rectangle iconRect, final Rectangle textRect, final boolean active, final boolean frameActive, final boolean isLeftToRight) {
+    protected void paintTabNormalFromRect(final Graphics g,
+                                          final int tabPlacement,
+                                          final Rectangle tabRect,
+                                          final int nonRectIndex,
+                                          final Rectangle iconRect,
+                                          final Rectangle textRect,
+                                          final boolean active,
+                                          final boolean frameActive,
+                                          final boolean isLeftToRight) {
         final int selectedIndex = tabPane.getSelectedIndex();
         final boolean isSelected = selectedIndex == nonRectIndex;
 
@@ -420,7 +439,12 @@
         paintContents(g, tabPlacement, nonRectIndex, tabRect, iconRect, textRect, isSelected);
     }
 
-    protected void paintCUITab(final Graphics g, final int tabPlacement, final Rectangle tabRect, final boolean isSelected, final boolean frameActive, final boolean isLeftToRight, final int nonRectIndex) {
+    protected void paintCUITab(final Graphics g, final int tabPlacement,
+                               final Rectangle tabRect,
+                               final boolean isSelected,
+                               final boolean frameActive,
+                               final boolean isLeftToRight,
+                               final int nonRectIndex) {
         final int tabCount = tabPane.getTabCount();
 
         final boolean needsLeftScrollTab = visibleTabState.needsLeftScrollTab();
@@ -835,14 +859,20 @@
         }
     }
 
-    public class MouseHandler extends MouseInputAdapter implements ActionListener {
-        protected int trackingTab = -3;
-        protected Timer popupTimer = new Timer(500, this);
+    class MouseHandler extends MouseInputAdapter implements ActionListener {
+
+        int trackingTab = -3;
+        private final Timer popupTimer = new Timer(500, this);
 
-        public MouseHandler() {
+        MouseHandler() {
             popupTimer.setRepeats(false);
         }
 
+        void dispose (){
+            popupTimer.removeActionListener(this);
+            popupTimer.stop();
+        }
+
         public void mousePressed(final MouseEvent e) {
             final JTabbedPane pane = (JTabbedPane)e.getSource();
             if (!pane.isEnabled()) {
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaTextAreaUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaTextAreaUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -42,6 +42,7 @@
     }
 
     AquaFocusHandler handler;
+    @Override
     protected void installListeners() {
         super.installListeners();
 
@@ -53,6 +54,7 @@
         AquaUtilControlSize.addSizePropertyListener(c);
     }
 
+    @Override
     protected void uninstallListeners() {
         final JTextComponent c = getComponent();
 
@@ -66,6 +68,7 @@
     }
 
     boolean oldDragState = false;
+    @Override
     protected void installDefaults() {
         if (!GraphicsEnvironment.isHeadless()) {
             oldDragState = getComponent().getDragEnabled();
@@ -74,6 +77,7 @@
         super.installDefaults();
     }
 
+    @Override
     protected void uninstallDefaults() {
         if (!GraphicsEnvironment.isHeadless()) {
             getComponent().setDragEnabled(oldDragState);
@@ -81,7 +85,9 @@
         super.uninstallDefaults();
     }
 
-    // Install a default keypress action which handles Cmd and Option keys properly
+    // Install a default keypress action which handles Cmd and Option keys
+    // properly
+    @Override
     protected void installKeyboardActions() {
         super.installKeyboardActions();
         AquaKeyBindings bindings = AquaKeyBindings.instance();
@@ -90,13 +96,12 @@
         bindings.installAquaUpDownActions(c);
     }
 
+    @Override
     protected Caret createCaret() {
-        final JTextComponent c = getComponent();
-        final Window owningWindow = SwingUtilities.getWindowAncestor(c);
-        final AquaCaret returnValue = new AquaCaret(owningWindow, c);
-        return returnValue;
+        return new AquaCaret();
     }
 
+    @Override
     protected Highlighter createHighlighter() {
         return new AquaHighlighter();
     }
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaTextFieldUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaTextFieldUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -42,6 +42,7 @@
     protected JComponentPainter delegate;
     protected AquaFocusHandler handler;
 
+    @Override
     protected void installListeners() {
         super.installListeners();
 
@@ -55,6 +56,7 @@
         AquaTextFieldSearch.installSearchFieldListener(c);
     }
 
+    @Override
     protected void uninstallListeners() {
         final JTextComponent c = getComponent();
         AquaTextFieldSearch.uninstallSearchFieldListener(c);
@@ -67,6 +69,7 @@
     }
 
     boolean oldDragState = false;
+    @Override
     protected void installDefaults() {
         if (!GraphicsEnvironment.isHeadless()) {
             oldDragState = getComponent().getDragEnabled();
@@ -76,6 +79,7 @@
         super.installDefaults();
     }
 
+    @Override
     protected void uninstallDefaults() {
         super.uninstallDefaults();
 
@@ -84,12 +88,15 @@
         }
     }
 
-    // Install a default keypress action which handles Cmd and Option keys properly
+    // Install a default keypress action which handles Cmd and Option keys
+    // properly
+    @Override
     protected void installKeyboardActions() {
         super.installKeyboardActions();
         AquaKeyBindings.instance().setDefaultAction(getKeymapName());
     }
 
+    @Override
     protected Rectangle getVisibleEditorRect() {
         final Rectangle rect = super.getVisibleEditorRect();
         if (rect == null) return null;
@@ -102,6 +109,7 @@
         return rect;
     }
 
+    @Override
     protected void paintSafely(final Graphics g) {
         paintBackgroundSafely(g);
         super.paintSafely(g);
@@ -149,20 +157,23 @@
 
         // the common case
         final int shrinkage = AquaTextFieldBorder.getShrinkageFor(c, height);
-        g.fillRect(insets.left - 2, insets.top - shrinkage - 1, width - insets.right - insets.left + 4, height - insets.bottom - insets.top + shrinkage * 2 + 2);
+        g.fillRect(insets.left - 2, insets.top - shrinkage - 1,
+                   width - insets.right - insets.left + 4,
+                   height - insets.bottom - insets.top + shrinkage * 2 + 2);
     }
 
+    @Override
     protected void paintBackground(final Graphics g) {
         // we have already ensured that the background is painted to our liking
         // by paintBackgroundSafely(), called from paintSafely().
     }
 
+    @Override
     protected Caret createCaret() {
-        final JTextComponent c = getComponent();
-        final Window owningWindow = SwingUtilities.getWindowAncestor(c);
-        return new AquaCaret(owningWindow, c);
+        return new AquaCaret();
     }
 
+    @Override
     protected Highlighter createHighlighter() {
         return new AquaHighlighter();
     }
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaTextPaneUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaTextPaneUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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
@@ -43,6 +43,7 @@
     }
 
     AquaFocusHandler handler;
+    @Override
     protected void installListeners() {
         super.installListeners();
         final JComponent c = getComponent();
@@ -52,6 +53,7 @@
         AquaUtilControlSize.addSizePropertyListener(c);
     }
 
+    @Override
     protected void uninstallListeners() {
         final JComponent c = getComponent();
         AquaUtilControlSize.removeSizePropertyListener(c);
@@ -62,6 +64,7 @@
     }
 
     boolean oldDragState = false;
+    @Override
     protected void installDefaults() {
         final JTextComponent c = getComponent();
         if (!GraphicsEnvironment.isHeadless()) {
@@ -71,6 +74,7 @@
         super.installDefaults();
     }
 
+    @Override
     protected void uninstallDefaults() {
         if (!GraphicsEnvironment.isHeadless()) {
             getComponent().setDragEnabled(oldDragState);
@@ -78,7 +82,9 @@
         super.uninstallDefaults();
     }
 
-    // Install a default keypress action which handles Cmd and Option keys properly
+    // Install a default keypress action which handles Cmd and Option keys
+    // properly
+    @Override
     protected void installKeyboardActions() {
         super.installKeyboardActions();
         AquaKeyBindings bindings = AquaKeyBindings.instance();
@@ -88,12 +94,12 @@
         bindings.installAquaUpDownActions(c);
     }
 
+    @Override
     protected Caret createCaret() {
-        final JTextComponent c = getComponent();
-        final Window owningWindow = SwingUtilities.getWindowAncestor(c);
-        return new AquaCaret(owningWindow, c);
+        return new AquaCaret();
     }
 
+    @Override
     protected Highlighter createHighlighter() {
         return new AquaHighlighter();
     }
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java	Mon Sep 07 23:29:56 2015 +0300
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java	Mon Sep 07 23:57:21 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -403,6 +403,7 @@
     protected void uninstallListeners() {
         header.removeMouseListener(mouseInputListener);
         header.removeMouseMotionListener(mouseInputListener);
+        header.removeFocusListener(focusListener);
 
         mouseInputListener = null;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java	Mon Sep 07 23:57:21 2015 +0300
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015, 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 java.awt.FlowLayout;
+
+import javax.swing.*;
+import javax.swing.UIManager.LookAndFeelInfo;
+
+import static javax.swing.UIManager.getInstalledLookAndFeels;
+
+/**
+ * @test
+ * @bug 8134947
+ * @author Sergey Bylokhov
+ * @run main/timeout=300/othervm -Xmx12m -XX:+HeapDumpOnOutOfMemoryError UnninstallUIMemoryLeaks
+ */
+public final class UnninstallUIMemoryLeaks {
+
+    private static JFrame frame;
+
+    public static void main(final String[] args) throws Exception {
+        try {
+            createGUI();
+            for (final LookAndFeelInfo laf : getInstalledLookAndFeels()) {
+                final String name = laf.getName();
+                if (name.contains("OS X") || name.contains("Metal")) {
+                    SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf));
+                    SwingUtilities.invokeAndWait(() -> {
+                        for (int i = 0; i < 4000; ++i) {
+                            SwingUtilities.updateComponentTreeUI(frame);
+                        }
+                    });
+                }
+            }
+        } finally {
+            EventQueue.invokeAndWait(() -> frame.dispose());
+        }
+    }
+
+    private static void createGUI() throws Exception {
+        EventQueue.invokeAndWait(() -> {
+            frame = new JFrame();
+            frame.setLayout(new FlowLayout());
+
+            frame.add(new JButton("JButton"));
+            frame.add(new JCheckBox("JCheckBox"));
+            frame.add(new JComboBox<>());
+            frame.add(new JEditorPane());
+            frame.add(new JFormattedTextField("JFormattedTextField"));
+            frame.add(new JLabel("label"));
+            frame.add(new JPanel());
+            frame.add(new JPasswordField("JPasswordField"));
+            frame.add(new JProgressBar());
+            frame.add(new JRadioButton("JRadioButton"));
+            frame.add(new JScrollBar());
+            frame.add(new JScrollPane());
+            frame.add(new JSeparator());
+            frame.add(new JSlider());
+            frame.add(new JSpinner());
+            frame.add(new JSplitPane());
+            frame.add(new JTabbedPane());
+            frame.add(new JTable());
+            frame.add(new JTextArea("JTextArea"));
+            frame.add(new JTextField("JTextField"));
+            frame.add(new JTextPane());
+            frame.add(new JToggleButton());
+            frame.add(new JToolBar());
+            frame.add(new JToolTip());
+            frame.add(new JTree());
+            frame.add(new JViewport());
+
+            final JMenuBar bar = new JMenuBar();
+            final JMenu menu1 = new JMenu("menu1");
+            final JMenu menu2 = new JMenu("menu2");
+            menu1.add(new JMenuItem("menuitem"));
+            menu2.add(new JCheckBoxMenuItem("JCheckBoxMenuItem"));
+            menu2.add(new JRadioButtonMenuItem("JRadioButtonMenuItem"));
+            bar.add(menu1);
+            bar.add(menu2);
+            frame.setJMenuBar(bar);
+
+            final String[] data = {"one", "two", "three", "four"};
+            final JList<String> list = new JList<>(data);
+            frame.add(list);
+
+            final JDesktopPane pane = new JDesktopPane();
+            final JInternalFrame internalFrame = new JInternalFrame();
+            internalFrame.setBounds(10, 10, 130, 130);
+            internalFrame.setVisible(true);
+            pane.add(internalFrame);
+            pane.setSize(150, 150);
+
+            frame.add(pane);
+            frame.pack();
+            frame.setSize(600, 600);
+            frame.setLocationRelativeTo(null);
+            // Commented to prevent a reference from RepaintManager
+            // frame.setVisible(true);
+        });
+    }
+
+    private static void setLookAndFeel(final LookAndFeelInfo laf) {
+        try {
+            UIManager.setLookAndFeel(laf.getClassName());
+            System.out.println("LookAndFeel: " + laf.getClassName());
+        } catch (ClassNotFoundException | InstantiationException |
+                UnsupportedLookAndFeelException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}