JDK-8182638 : [macosx] Active modal dialog is hidden by another non-active one
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u131,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86
  • Submitted: 2017-06-20
  • Updated: 2017-11-03
  • Resolved: 2017-10-16
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 10
10 b29Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
$ /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
$ uname -a
Darwin unit-929 16.6.0 Darwin Kernel Version 16.6.0: Fri Apr 14 16:21:16 PDT 2017; root:xnu-3789.60.24~6/RELEASE_X86_64 x86_64

A DESCRIPTION OF THE PROBLEM :
Active modal dialog is hidden by another non-active one

REGRESSION.  Last worked in version 8u112

ADDITIONAL REGRESSION INFORMATION: 
It was not checked on 1.8.0_121, 9 EA

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) create modal dialog D1
2) set any modality type e.g. APPLICATION_MODAL
3) create modal dialog D2
4) set the modality type APPLICATION_MODAL


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
D2 is active and it is placed on top of all windows including D1.
ACTUAL -
D2 is active but D1 is placed on top of D2.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Copyright 2000-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import javax.imageio.ImageIO;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.AWTException;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Rectangle;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

/* @test
 * @summary regression test on JRE-392 Tip of the day is not hidden while another modal window is shown
 * @run main/othervm JDialog392
 */


// The test displays two modal dialogs one by one
// and checks that the latest modal dialog would be on top of all windows
public class JDialog392 implements Runnable {

    private static JFrame frame = new JFrame("JDialog392");

    private static boolean verbose = false;
    private static boolean passed = true;

    static DialogThread modalDialogThread1;

    static DialogThread modalDialogThread2;

    static class DialogThread {

        JDialog dialog;

        private String dialogTitle;
        private Point location;
        private int width;
        private int height;
        private DialogListener eventListener;


        DialogThread(String dialogTitle, Point location, int width, int height, DialogListener eventListener) {
            this.dialogTitle = dialogTitle;
            this.location = location;
            this.width = width;
            this.height = height;
            this.eventListener = eventListener;
        }

        void run() {
            dialog = new JDialog(frame, true);
            dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
            dialog.setTitle(dialogTitle);

            dialog.setLocation(location);
            dialog.setSize(width, height);

            if (eventListener != null)
                dialog.addWindowListener(eventListener);

            dialog.setVisible(true);
        }

        void removeWindowListener() {
            dialog.removeWindowListener(eventListener);
        }
    }

    static abstract class DialogListener implements WindowListener {

        @Override
        public void windowClosing(WindowEvent e) {

        }

        @Override
        public void windowClosed(WindowEvent e) {

        }

        @Override
        public void windowIconified(WindowEvent e) {

        }

        @Override
        public void windowDeiconified(WindowEvent e) {

        }

        @Override
        public void windowActivated(WindowEvent e) {

        }

        @Override
        public void windowDeactivated(WindowEvent e) {

        }
    }

    static class FirstDialogListener extends DialogListener {
        @Override
        public void windowOpened(WindowEvent windowEvent) {
            modalDialogThread1.removeWindowListener();
            modalDialogThread2 = new DialogThread(
                    "Modal input 2",
                    new Point(5, 50),
                    300, 200,
                    new SecondDialogListener());
            modalDialogThread2.run();
        }
    }

    static class SecondDialogListener extends DialogListener {
        @Override
        public void windowOpened(WindowEvent windowEvent) {
            try {
                Robot robot = new Robot();
                Dimension shotSize = modalDialogThread2.dialog.getContentPane().getSize();
                Rectangle captureRect = new Rectangle(modalDialogThread2.dialog.getContentPane().getLocationOnScreen(), shotSize);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                BufferedImage screenImage = robot.createScreenCapture(captureRect);

                int rgb;
                int expectedRGB = screenImage.getRGB((int) (shotSize.getWidth() / 2), (int) (shotSize.getHeight() / 2)) & 0x00FFFFFF;

                for (int col = 1; col < shotSize.getWidth(); col++) {
                    for (int row = 1; row < shotSize.getHeight(); row++) {
                        try {
                            // remove transparance
                            rgb = screenImage.getRGB(col, row) & 0x00FFFFFF;

                            if (verbose)
                                System.out.print((rgb == expectedRGB) ? " ." : " " + Integer.toHexString(rgb));

                            passed = passed & (expectedRGB == rgb);

                        } catch (ArrayIndexOutOfBoundsException e) {
                            throw new RuntimeException(e);
                        }

                    }
                    if (verbose)
                        System.out.println();
                }
                if (verbose)
                    ImageIO.write(screenImage, "bmp", new File("test392.bmp"));

                if (!passed)
                    throw new RuntimeException("The second dialog window was not on top");

            } catch (AWTException | IOException e) {
                throw new RuntimeException(e);
            }
            modalDialogThread2.dialog.dispose();
            modalDialogThread1.dialog.dispose();
            frame.dispose();
        }
    }

    public void run() {
        frame.setSize(350, 300);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

        modalDialogThread1 = new DialogThread(
                "Modal input 1",
                new Point(10, 75),
                250, 150,
                new FirstDialogListener());
        modalDialogThread1.run();
    }

    public static void main(String[] args) throws Exception {
        JDialog392.verbose = Arrays.asList(args).contains("-verbose");
        try {
            SwingUtilities.invokeAndWait(new JDialog392());
        } catch (InterruptedException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

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


Comments
I have tested this bug a little bit. Initially the provided example works as described in the CR. But if the user will move the windows, or tries to select some of them, then the test start to work as expected(the bug disappears). Looks like the bug is reproduced when the windows initially are shown.
22-06-2017

This is the current expected behaviour. Fixed in 8u131.
21-06-2017

Checked this on MAC OS X 10.12.5 and could confirm the regression as reported. The issue is reproducible with JDK 8u131 and 9 ea b174. Result: ============= 8u112: OK 8u121: OK 8u131: FAIL 8u152 ea b04: FAIL 9 ea b174: FAIL To verify run the attached test case with respective JDK version. With 8u131 and 9 ea b174, dialog D2 is active but D1 is placed on top of D2, where as with 8u121 expected outcome with D2 being active and placed on top of all windows including D1. This seems linked to the fix introduced with JDK-8169589
21-06-2017