From e51a3fcafb80e10dc18e38060608bc70d150610d Mon Sep 17 00:00:00 2001 From: anthony Date: Tue, 3 Apr 2012 16:14:05 +0400 Subject: [PATCH] 7154177: [macosx] An invisible owner frame becomes visible upon clicking a child window Summary: Establish the parent-child relationship for visible windows only Reviewed-by: serb --- .../sun/lwawt/macosx/CPlatformWindow.java | 74 +++++++++-- .../Frame/InvisibleOwner/InvisibleOwner.java | 121 ++++++++++++++++++ 2 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 test/java/awt/Frame/InvisibleOwner/InvisibleOwner.java diff --git a/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index bcd6207ae..e2119f5f4 100644 --- a/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -29,6 +29,7 @@ import java.awt.BufferCapabilities.FlipContents; import java.awt.*; import java.awt.Dialog.ModalityType; import java.awt.event.*; +import java.awt.peer.WindowPeer; import java.beans.*; import java.util.List; @@ -202,6 +203,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo private LWWindowPeer peer; private CPlatformView contentView; private CPlatformWindow owner; + private boolean visible = false; // visibility status from native perspective private boolean undecorated; // initialized in getInitialStyleBits() private Rectangle normalBounds = null; // not-null only for undecorated maximized windows @@ -492,19 +494,38 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo } } + private boolean isVisible() { + return this.visible; + } + @Override // PlatformWindow public void setVisible(boolean visible) { final long nsWindowPtr = getNSWindowPtr(); - if (owner != null) { - if (!visible) { + // 1. Process parent-child relationship when hiding + if (!visible) { + // 1a. Unparent my children + for (Window w : target.getOwnedWindows()) { + WindowPeer p = (WindowPeer)w.getPeer(); + if (p instanceof LWWindowPeer) { + CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow(); + if (pw != null && pw.isVisible()) { + CWrapper.NSWindow.removeChildWindow(nsWindowPtr, pw.getNSWindowPtr()); + } + } + } + + // 1b. Unparent myself + if (owner != null && owner.isVisible()) { CWrapper.NSWindow.removeChildWindow(owner.getNSWindowPtr(), nsWindowPtr); } } + // 2. Configure stuff updateIconImages(); updateFocusabilityForAutoRequestFocus(false); + // 3. Manage the extended state when hiding if (!visible) { // Cancel out the current native state of the window switch (peer.getState()) { @@ -517,6 +538,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo } } + // 4. Actually show or hide the window LWWindowPeer blocker = peer.getBlocker(); if (blocker == null || !visible) { // If it ain't blocked, or is being hidden, go regular way @@ -543,7 +565,9 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo CWrapper.NSWindow.orderWindow(nsWindowPtr, CWrapper.NSWindow.NSWindowBelow, ((CPlatformWindow)blocker.getPlatformWindow()).getNSWindowPtr()); } + this.visible = visible; + // 5. Manage the extended state when showing if (visible) { // Re-apply the extended state as expected in shared code if (target instanceof Frame) { @@ -558,17 +582,35 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo } } + // 6. Configure stuff #2 updateFocusabilityForAutoRequestFocus(true); - if (owner != null) { - if (visible) { + // 7. Manage parent-child relationship when showing + if (visible) { + // 7a. Add myself as a child + if (owner != null && owner.isVisible()) { CWrapper.NSWindow.addChildWindow(owner.getNSWindowPtr(), nsWindowPtr, CWrapper.NSWindow.NSWindowAbove); if (target.isAlwaysOnTop()) { CWrapper.NSWindow.setLevel(nsWindowPtr, CWrapper.NSWindow.NSFloatingWindowLevel); } } + + // 7b. Add my own children to myself + for (Window w : target.getOwnedWindows()) { + WindowPeer p = (WindowPeer)w.getPeer(); + if (p instanceof LWWindowPeer) { + CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow(); + if (pw != null && pw.isVisible()) { + CWrapper.NSWindow.addChildWindow(nsWindowPtr, pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove); + if (w.isAlwaysOnTop()) { + CWrapper.NSWindow.setLevel(pw.getNSWindowPtr(), CWrapper.NSWindow.NSFloatingWindowLevel); + } + } + } + } } + // 8. Deal with the blocker of the window being shown if (blocker != null && visible) { // Make sure the blocker is above its siblings ((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings(); @@ -875,15 +917,23 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo return; } - // Recursively pop up the windows from the very bottom so that only - // the very top-most one becomes the main window - owner.orderAboveSiblings(); + // NOTE: the logic will fail if we have a hierarchy like: + // visible root owner + // invisible owner + // visible dialog + // However, this is an unlikely scenario for real life apps + if (owner.isVisible()) { + // Recursively pop up the windows from the very bottom so that only + // the very top-most one becomes the main window + owner.orderAboveSiblings(); + + // Order the window to front of the stack of child windows + final long nsWindowSelfPtr = getNSWindowPtr(); + final long nsWindowOwnerPtr = owner.getNSWindowPtr(); + CWrapper.NSWindow.removeChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr); + CWrapper.NSWindow.addChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove); + } - // Order the window to front of the stack of child windows - final long nsWindowSelfPtr = getNSWindowPtr(); - final long nsWindowOwnerPtr = owner.getNSWindowPtr(); - CWrapper.NSWindow.removeChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr); - CWrapper.NSWindow.addChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove); if (target.isAlwaysOnTop()) { CWrapper.NSWindow.setLevel(getNSWindowPtr(), CWrapper.NSWindow.NSFloatingWindowLevel); } diff --git a/test/java/awt/Frame/InvisibleOwner/InvisibleOwner.java b/test/java/awt/Frame/InvisibleOwner/InvisibleOwner.java new file mode 100644 index 000000000..007f14d17 --- /dev/null +++ b/test/java/awt/Frame/InvisibleOwner/InvisibleOwner.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2012, 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 7154177 + @summary An invisible owner frame should never become visible + @author anthony.petrov@oracle.com: area=awt.toplevel + @library ../../regtesthelpers + @build Util + @run main InvisibleOwner +*/ + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import test.java.awt.regtesthelpers.Util; + +public class InvisibleOwner { + private static volatile boolean invisibleOwnerClicked = false; + private static volatile boolean backgroundClicked = false; + + private static final int F_X = 40, F_Y = 40, F_W = 200, F_H = 200; + + public static void main(String[] args) throws AWTException { + // A background frame to compare a pixel color against + Frame helperFrame = new Frame("Background frame"); + helperFrame.setBackground(Color.BLUE); + helperFrame.setBounds(F_X - 10, F_Y - 10, F_W + 20, F_H + 20); + helperFrame.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent ev) { + backgroundClicked= true; + } + }); + helperFrame.setVisible(true); + + // An owner frame that should stay invisible + Frame frame = new Frame("Invisible Frame"); + frame.setBackground(Color.GREEN); + frame.setLocation(F_X, F_Y); + frame.setSize(F_W, F_H); + frame.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent ev) { + invisibleOwnerClicked = true; + } + }); + + // An owned window + final Window window = new Window(frame); + window.setBackground(Color.RED); + window.setSize(200, 200); + window.setLocation(300, 300); + window.setVisible(true); + try { Thread.sleep(1000); } catch (Exception ex) {} + + Robot robot = new Robot(); + + // Clicking the owned window shouldn't make its owner visible + Util.clickOnComp(window, robot); + try { Thread.sleep(500); } catch (Exception ex) {} + + + // Assume the location and size are applied to the frame as expected. + // This should work fine on the Mac. We can't call getLocationOnScreen() + // since from Java perspective the frame is invisible anyway. + + // 1. Check the color at the center of the owner frame + Color c = robot.getPixelColor(F_X + F_W / 2, F_Y + F_H / 2); + System.err.println("Pixel color: " + c); + if (c == null) { + throw new RuntimeException("Robot.getPixelColor() failed"); + } + if (c.equals(frame.getBackground())) { + throw new RuntimeException("The invisible frame has become visible"); + } + if (!c.equals(helperFrame.getBackground())) { + throw new RuntimeException("The background helper frame has been covered by something unexpected"); + } + + // 2. Try to click it + robot.mouseMove(F_X + F_W / 2, F_Y + F_H / 2); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + try { Thread.sleep(500); } catch (Exception ex) {} + + // Cleanup + window.dispose(); + frame.dispose(); + helperFrame.dispose(); + + // Final checks + if (invisibleOwnerClicked) { + throw new RuntimeException("An invisible owner frame got clicked. Looks like it became visible."); + } + if (!backgroundClicked) { + throw new RuntimeException("The background helper frame hasn't been clciked"); + } + } +} -- GitLab