United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-8013611 : Modal dialog fails to obtain keyboard focus

Details
Type:
Bug
Submit Date:
2013-02-07
Status:
Closed
Updated Date:
2014-09-04
Project Name:
JDK
Resolved Date:
2013-08-08
Component:
client-libs
OS:
windows
Sub-Component:
javax.swing
CPU:
generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
7u13
Fixed Versions:

Related Reports
Backport:
Backport:
Backport:
Backport:
Backport:
Backport:
Backport:
Backport:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version  " 1.7.0_13 " 
Java(TM) SE Runtime Environment (build 1.7.0_13-b20)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 7 Enterprise Service Pack 1

A DESCRIPTION OF THE PROBLEM :
If a modal dialog is displayed from a focus listener and at the same time some modifications to focus are done in the frame in background then the dialog will fail to obtain focus. Both calls to requestFocusInWindow and use of FocusTraversalPolicy seems to provoke this bug.

Stepping in the debugger it looks like the dialog is processing focusing events not related to the dialog but instead related to the frame in background which makes the focus state broken.

REGRESSION.  Last worked in version 6u31

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The following steps will produce the problem

- Add a focus listener to a JTextField
- In the focusLost() method display an modal dialog
- In the next component that will receive focus add a focus listener and in the focusGained() method call requestFocusInWindow() for any other component in the same frame

When focus is lost on the text field the dialog fails to gain focus and the focus is instead given to the component called for requestFocusInWindow()

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Focus placed on the default component inside the modal dialog
ACTUAL -
Focus is placed in the frame in the background, from the keyboard it's possible to tab around in the frame in the background and also to activate buttons by pressing enter on keyboard.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
 *
 * @author jonas
 */
public class SwingFocusBug extends JFrame {

    JTextField textField = new JTextField( " Press <tab> on keyboard " );
    JButton button1 = new JButton( " 1 " );
    JButton button2 = new JButton( " 2 " );

    public SwingFocusBug() {
        initComponents();

        textField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                JOptionPane.showMessageDialog(SwingFocusBug.this,
                         " Focus is now in frame behind and not in the dialog! " );
            }
        });

        button1.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                button2.requestFocusInWindow();
            }
        });

        button2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                button2.setText( " Button 2 pressed! " );
            }
        });
    }

    private void initComponents() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(textField, BorderLayout.CENTER);
        panel.add(button1, BorderLayout.EAST);
        panel.add(button2, BorderLayout.SOUTH);

        add(panel);

        setSize(new Dimension(500, 200));

    }

    public static void main(String[] args) throws ClassNotFoundException,
            InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {

        UIManager.setLookAndFeel( " com.sun.java.swing.plaf.windows.WindowsLookAndFeel " );

        SwingFocusBug frame = new SwingFocusBug();
        frame.setVisible(true);


    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
In the focus listener displaying the dialog, make sure all events related to focus changes in the parent frame are dispatch by the event queue by wrapping the code in at least two SwingUtils.invokeLater blocks:

    private FocusListener fieldFocusListener = new FocusAdapter() {
        @Override
        public void focusLost(FocusEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            // code that will display a dialog
                        }
                    });
                }
            });
        }
    };
                                    

Comments
The bug was reproduced with JDK 7u21 on MS Windows 7 OS.
                                     
2013-04-30
There're two method KeyboardFocusManagerPeer.getCurrentFocusOwner() & KeyboardFocusManagerPeer.getCurrentFocusedWindow() which return values of current native focus owner and current native focused window. The problem is that the value of a native focused window changes synchronously with the native message receiving. Whereas, the value of current native focus owner is set later after it is calculated on java side and is reported to peers.

In the testcase, showing the dialog and requesting focus in frame just happens b/w the two events mentioned above. So, the native focused window has already changed but the native focus owner has not yet. At that moment, KFM.shouldNativelyFocusHeavyweight() method is called in response to requestFocusInWindow(). (The method checks for the focus subsystem current state (pending focus requests, current native focus owner, native focused window etc.) and decides how the request should be processed.) It gets native focus owner and checks if it equals the heavyweight container of the component requesting focus. If it does equal, it just adds a lightweight component to the focus requests queue and posts corresponding focus events. The problem is that the current native focus owner is outdated at the moment. It doesn't correspond to the current native focused window. And so, the requestFocusInWindow() is satisfied, focus events are posted.

In order to avoid that, I suggest to additionally check if the parent window of the component requesting focus equals the current native focused window. In case they aren't, requestFocusInWindow() should be rejected.
                                     
2013-07-03
webrev: http://cr.openjdk.java.net/~ant/JDK-8013611/webrev.0/
                                     
2013-07-03
URL:   http://hg.openjdk.java.net/jdk8/awt/jdk/rev/903a279f1fce
User:  ant
Date:  2013-08-08 09:21:26 +0000

                                     
2013-08-08
URL:   http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/903a279f1fce
User:  lana
Date:  2013-08-26 18:32:19 +0000

                                     
2013-08-26
Verified using regression test test/java/awt/Focus/8013611/JDK8013611.java   
jdk8u100: Test failed: dialog didn't get focus!
jdk8b132: Passed. Execution successfull
                                     
2014-04-14
SQE tested the fix in 7u55 b32. Swing results are ok. 
So, SQE OK to take the fix into CPU14_03.
                                     
2014-04-14



Hardware and Software, Engineered to Work Together