JDK-8006417 : JComboBox.showPopup(), hidePopup() fails in JRE 1.7 on OS X
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7
  • Priority: P1
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: generic
  • Submitted: 2013-01-16
  • Updated: 2013-09-12
  • Resolved: 2013-01-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u11Fixed 8Fixed
Description
FULL PRODUCT VERSION :
JDK 7u9

ADDITIONAL OS VERSION INFORMATION :
Apple Mac OS X 10.7 (Intel) (64-bit)

A DESCRIPTION OF THE PROBLEM :
An immediate call to JComboBox.showPopup() after JComboBox.hidePopup() does not lead to showing of a popup menu in JDK 7 on OS X, instead of this the popup menu just flashes. However the popup menu is always shown in JDK 6.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the attached source code.
2. Click into any place of a displayed frame.

REPRODUCIBILITY :
The bug is reproduced always.

---------- BEGIN SOURCE ---------- 
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

public class TestWTDComboBox {
    static JComboBox kb = null;
    static boolean dataPopulated = false;
    static AWTEvent event = null;
    static JFrame frame;

    public static void main(String[] args) {
        frame = new JFrame("Creating a JComboBox Component");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 400);

        JPanel panel = new JPanel();
        String anArray[] = {"BCA", "MCA", "PPC", "CIC"};
        kb = new JComboBox(anArray) {
            @Override
            public void showPopup() {
                if (!dataPopulated) {
                    frame.dispatchEvent(event);
                }
                super.showPopup();
            }
        };
        kb.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                System.out.println("hello");
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                kb.setPopupVisible(false);
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {}
        });
        panel.add(kb);
        frame.add(panel);
        frame.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("hello111");
                event = e;
                test();
            }

            @Override
            public void mousePressed(MouseEvent e) {
                System.out.println("hello111");
                event = e;
                test();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                System.out.println("hello111");
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                System.out.println("hello111");
            }

            @Override
            public void mouseExited(MouseEvent e) {
                System.out.println("hello111");
            }
        });

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                frame.setVisible(true);
            }
        });
    }

    public static void test() {
        Object prevSel = kb.getSelectedItem();
        boolean isDropDown = kb.isPopupVisible();
        kb.hidePopup();
        kb.removeAllItems();

        kb.addItem("none");
        kb.addItem("none1");
        kb.addItem("none2");
        kb.addItem("none3");
        dataPopulated = true;
        kb.setSelectedItem(prevSel);

        kb.showPopup();
        dataPopulated = false;
    }
}
---------- END SOURCE ----------
Comments
Used supplied java test (source code). Tested under JDK 7u40 b27 on Mac Mini OSX 10.8.3. No issues encountered. Closing this issue as Verified.
05-06-2013

The issue was successfully reproduced on: - JDK 7u12 b06 (latest) - JDK 7u4 b11 (the first JDK 7 from Oracle) - JDK 8 b69 This issue happens, because of concurrency between internal processing of calls to JComboBox.hidePopup() and JComboBox.showPopup() methods. A deep research showed the following facts: 1. Processing of "sun.awt.UngrabEvent" cancels JPopupMenu of JComboBox, what is the reason of this bug. 2. A call to JComboBox.hidePopup() generates "sun.awt.UngrabEvent", which is processed asynchronously, in its underlying code (the method "public void stateChanged(ChangeEvent e)" of javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber) in JDK 7, JDK 8 on OS X. 3. Behavior from point #2 does not happen neither on Windows OS, nor on Linux OS, because of a particular implementation of "ungrab(Window w)" method of "sun.awt.SunToolkit" for OS X. A pseudo code which describes a process leading to this bug is presented below. 1. JComboBox.hidePopup() - (the popup menu becomes hidden) ... ((sun.awt.SunToolkit)tk).ungrab(grabbedWindow) .... "sun.awt.UngrabEvent" is posted to AWT event queue. .... 2. JComboBox.showPopup() - (the popup menu becomes shown) 3. "sun.awt.UngrabEvent" is dispatched to JFrame - (the popup menu becomes hidden again) .... Any shown popup is closed. .... One of possible solutions could be not posting of "sun.awt.UngrabEvent" by "sun.lwawt.LWWindowPeer.ungrab()", when it is called from "sun.lwawt.LWToolkit". A similar strategy is applied in native code of JDK for Windows OS.
16-01-2013