Mercurial > hg > jdk9-shenandoah > jdk
changeset 12765:4c078d8904c9
8134947: [macosx] Various memory leaks in Aqua look and feel
Reviewed-by: azvegint, alexsch
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); + } + } +}