8007220: [macosx] Setting popupmenu on TrayIcon do not work if done *after* adding TrayIcon
authorpchelko
Fri, 20 Dec 2013 14:42:37 +0400
changeset 23247 dd6c58eeb05c
parent 23246 c85233c6357c
child 23248 3418855fb2c5
8007220: [macosx] Setting popupmenu on TrayIcon do not work if done *after* adding TrayIcon Reviewed-by: anthony, serb
jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java
jdk/test/java/awt/TrayIcon/AddPopupAfterShowTest/AddPopupAfterShowTest.html
jdk/test/java/awt/TrayIcon/AddPopupAfterShowTest/AddPopupAfterShowTest.java
jdk/test/java/awt/TrayIcon/PopupMenuLeakTest/PopupMenuLeakTest.java
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java	Thu Dec 19 16:49:27 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java	Fri Dec 20 14:42:37 2013 +0400
@@ -76,8 +76,9 @@
                 menuPeer = (CPopupMenu)popup.getPeer();
                 if (menuPeer == null) {
                     popup.addNotify();
+                    menuPeer = (CPopupMenu)popup.getPeer();
                 }
-            }catch (Exception e){
+            } catch (Exception e) {
                 e.printStackTrace();
             }
         }
@@ -97,7 +98,12 @@
     //invocation from the AWTTrayIcon.m
     public long getPopupMenuModel(){
         if(popup == null) {
-            return 0L;
+            PopupMenu popupMenu = target.getPopupMenu();
+            if (popupMenu != null) {
+                popup = popupMenu;
+            } else {
+                return 0L;
+            }
         }
         return checkAndCreatePopupPeer().getModel();
     }
@@ -134,6 +140,10 @@
 
         dummyFrame.dispose();
 
+        if (popup != null) {
+            popup.removeNotify();
+        }
+
         LWCToolkit.targetDisposedPeer(target, this);
         target = null;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TrayIcon/AddPopupAfterShowTest/AddPopupAfterShowTest.html	Fri Dec 20 14:42:37 2013 +0400
@@ -0,0 +1,45 @@
+<!--
+ Copyright (c) 2013, 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.
+-->
+
+<html>
+<!--
+  @test
+  @bug 8007220
+  @summary The popup menu is not added to the tray icon after it was added to tray
+  @author Petr Pchelko
+  @library ../../regtesthelpers
+  @build Sysout
+  @run applet/manual=yesno AddPopupAfterShowTest.html
+  -->
+<head>
+    <title> AddPopupAfterShowTest </title>
+</head>
+<body>
+
+<h1>AddPopupAfterShowTest<br>Bug ID: 8007220</h1>
+
+<p> See the dialog box (usually in upper left corner) for instructions</p>
+
+<APPLET CODE="AddPopupAfterShowTest.class" WIDTH=200 HEIGHT=200></APPLET>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TrayIcon/AddPopupAfterShowTest/AddPopupAfterShowTest.java	Fri Dec 20 14:42:37 2013 +0400
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013, 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 test.java.awt.regtesthelpers.Sysout;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class AddPopupAfterShowTest extends Applet {
+    @Override
+    public void init() {
+        if (!SystemTray.isSupported()) {
+            Sysout.createDialogWithInstructions(new String[]{
+                    "Press PASS, the System Tray is not supported"});
+            return;
+        }
+
+
+        String[] instructions = {
+                "1) The red circle icon was added to the system tray.",
+                "2) Check that a popup menu is opened when the icon is clicked.",
+                "3) If true the test is passed, otherwise failed."};
+        Sysout.createDialogWithInstructions(instructions);
+    }
+
+    @Override
+    public void start() {
+        setSize(200, 200);
+        show();
+
+        createSystemTrayIcon();
+    }
+
+    private static void createSystemTrayIcon() {
+        final TrayIcon trayIcon = new TrayIcon(createTrayIconImage());
+        trayIcon.setImageAutoSize(true);
+
+        try {
+            // Add tray icon to system tray *before* adding popup menu to demonstrate buggy behaviour
+            SystemTray.getSystemTray().add(trayIcon);
+            trayIcon.setPopupMenu(createTrayIconPopupMenu());
+        } catch (final AWTException awte) {
+            awte.printStackTrace();
+        }
+    }
+
+    private static Image createTrayIconImage() {
+        /**
+         * Create a small image of a red circle to use as the icon for the tray icon
+         */
+        int trayIconImageSize = 32;
+        final BufferedImage trayImage = new BufferedImage(trayIconImageSize, trayIconImageSize, BufferedImage.TYPE_INT_ARGB);
+        final Graphics2D trayImageGraphics = (Graphics2D) trayImage.getGraphics();
+
+        trayImageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+        trayImageGraphics.setColor(new Color(255, 255, 255, 0));
+        trayImageGraphics.fillRect(0, 0, trayImage.getWidth(), trayImage.getHeight());
+
+        trayImageGraphics.setColor(Color.red);
+
+        int trayIconImageInset = 4;
+        trayImageGraphics.fillOval(trayIconImageInset,
+                trayIconImageInset,
+                trayImage.getWidth() - 2 * trayIconImageInset,
+                trayImage.getHeight() - 2 * trayIconImageInset);
+
+        trayImageGraphics.setColor(Color.darkGray);
+
+        trayImageGraphics.drawOval(trayIconImageInset,
+                trayIconImageInset,
+                trayImage.getWidth() - 2 * trayIconImageInset,
+                trayImage.getHeight() - 2 * trayIconImageInset);
+
+        return trayImage;
+    }
+
+    private static PopupMenu createTrayIconPopupMenu() {
+        final PopupMenu trayIconPopupMenu = new PopupMenu();
+        final MenuItem popupMenuItem = new MenuItem("TEST PASSED!");
+        trayIconPopupMenu.add(popupMenuItem);
+        return trayIconPopupMenu;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TrayIcon/PopupMenuLeakTest/PopupMenuLeakTest.java	Fri Dec 20 14:42:37 2013 +0400
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+  @bug 8007220
+  @summary Reference to the popup leaks after the TrayIcon is removed
+  @author Petr Pchelko
+  @run main/othervm -Xmx50m PopupMenuLeakTest
+ */
+
+import java.awt.*;
+import javax.swing.SwingUtilities;
+import sun.awt.SunToolkit;
+
+import java.awt.image.BufferedImage;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class PopupMenuLeakTest {
+
+    static final AtomicReference<WeakReference<TrayIcon>> iconWeakReference = new AtomicReference<>();
+    static final AtomicReference<WeakReference<PopupMenu>> popupWeakReference = new AtomicReference<>();
+
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(PopupMenuLeakTest::createSystemTrayIcon);
+        sleep();
+        // To make the test automatic we explicitly call addNotify on a popup to create the peer
+        SwingUtilities.invokeAndWait(PopupMenuLeakTest::addNotifyPopup);
+        sleep();
+        SwingUtilities.invokeAndWait(PopupMenuLeakTest::removeIcon);
+        sleep();
+        assertCollected(popupWeakReference.get(), "Failed, reference to popup not collected");
+        assertCollected(iconWeakReference.get(), "Failed, reference to tray icon not collected");
+    }
+
+    private static void addNotifyPopup() {
+        PopupMenu menu = popupWeakReference.get().get();
+        if (menu == null) {
+            throw new RuntimeException("Failed: popup collected too early");
+        }
+        menu.addNotify();
+    }
+
+    private static void removeIcon() {
+        TrayIcon icon = iconWeakReference.get().get();
+        if (icon == null) {
+            throw new RuntimeException("Failed: TrayIcon collected too early");
+        }
+        SystemTray.getSystemTray().remove(icon);
+    }
+
+    private static void assertCollected(WeakReference<?> reference, String message) {
+        java.util.List<byte[]> bytes = new ArrayList<>();
+        for (int i = 0; i < 5; i ++) {
+            try {
+                while (true) {
+                    bytes.add(new byte[1024]);
+                }
+            } catch (OutOfMemoryError err) {
+                bytes = new ArrayList<>();
+            }
+        }
+        if (reference.get() != null) {
+            throw new RuntimeException(message);
+        }
+    }
+
+    private static void createSystemTrayIcon() {
+        final TrayIcon trayIcon = new TrayIcon(createTrayIconImage());
+        trayIcon.setImageAutoSize(true);
+
+        try {
+            // Add tray icon to system tray *before* adding popup menu to demonstrate buggy behaviour
+            trayIcon.setPopupMenu(createTrayIconPopupMenu());
+            SystemTray.getSystemTray().add(trayIcon);
+            iconWeakReference.set(new WeakReference<>(trayIcon));
+            popupWeakReference.set(new WeakReference<>(trayIcon.getPopupMenu()));
+        } catch (final AWTException awte) {
+            awte.printStackTrace();
+        }
+    }
+
+    private static Image createTrayIconImage() {
+        /**
+         * Create a small image of a red circle to use as the icon for the tray icon
+         */
+        int trayIconImageSize = 32;
+        final BufferedImage trayImage = new BufferedImage(trayIconImageSize, trayIconImageSize, BufferedImage.TYPE_INT_ARGB);
+        final Graphics2D trayImageGraphics = (Graphics2D) trayImage.getGraphics();
+
+        trayImageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+        trayImageGraphics.setColor(new Color(255, 255, 255, 0));
+        trayImageGraphics.fillRect(0, 0, trayImage.getWidth(), trayImage.getHeight());
+
+        trayImageGraphics.setColor(Color.red);
+
+        int trayIconImageInset = 4;
+        trayImageGraphics.fillOval(trayIconImageInset,
+                trayIconImageInset,
+                trayImage.getWidth() - 2 * trayIconImageInset,
+                trayImage.getHeight() - 2 * trayIconImageInset);
+
+        trayImageGraphics.setColor(Color.darkGray);
+
+        trayImageGraphics.drawOval(trayIconImageInset,
+                trayIconImageInset,
+                trayImage.getWidth() - 2 * trayIconImageInset,
+                trayImage.getHeight() - 2 * trayIconImageInset);
+
+        return trayImage;
+    }
+
+    private static PopupMenu createTrayIconPopupMenu() {
+        final PopupMenu trayIconPopupMenu = new PopupMenu();
+        final MenuItem popupMenuItem = new MenuItem("TEST!");
+        trayIconPopupMenu.add(popupMenuItem);
+        return trayIconPopupMenu;
+    }
+
+    private static void sleep() {
+        ((SunToolkit)Toolkit.getDefaultToolkit()).realSync();
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException ignored) { }
+    }
+}