JDK-8144680 : Stage.alwaysOnTop() doesn't work if a security manager is set
  • Type: Bug
  • Component: javafx
  • Sub-Component: scenegraph
  • Affected Version: 8u45,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-12-01
  • Updated: 2020-01-31
  • Resolved: 2015-12-14
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 8 JDK 9
8u92Fixed 9Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
1.8.0_45

ADDITIONAL OS VERSION INFORMATION :
Windows 6.1.7601

A DESCRIPTION OF THE PROBLEM :
If using a JFXPanel withing a Swing application, setting "alwaysOnTop" to true for a Stage does not work if a security manager has been set, even if that manager does not restrict any permissions. This doesn't seem to be an issue with pure JavaFX applications, and it works fine if no security manager is set

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code attached and click the "Show Alert" button in the popup window

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The alert that pops up should be always on top of any other window
ACTUAL -
The alert can be hidden by any other window including the man application window. If however the first in in the main() method (which sets the DoNothingSecurityManager) is commented out the alert will always be on top

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.security.Permission;
import javax.swing.*;

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class JFXPanelAlwaysOnTopDialogTest {

  public static void main(String[] args) {
    System.setSecurityManager(new DoNothingSecurityManager());
    SwingUtilities.invokeLater(JFXPanelAlwaysOnTopDialogTest::initAndShowUI);
  }

  private static void initAndShowUI() {
    JFrame frame = new JFrame("Test");
    JFXPanel jfxPanel = new JFXPanel();
    frame.add(jfxPanel);
    frame.setSize(100, 100);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Platform.runLater(() -> initFX(jfxPanel));
  }

  private static void initFX(JFXPanel jfxPanel) {
    Button button = new Button("Show Alert");
    button.setOnAction(event -> showAlert());
    Group root = new Group(button);
    jfxPanel.setScene(new Scene(root));
  }

  private static void showAlert() {
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    Stage stage = (Stage)alert.getDialogPane().getScene().getWindow();
    stage.setAlwaysOnTop(true);
    alert.setContentText("Here is my alert");
    alert.showAndWait();
  }

  private static class DoNothingSecurityManager extends SecurityManager {

    public void checkPermission(Permission perm) {}
    public void checkPermission(Permission perm, Object context) {}
    public void checkCreateClassLoader() {}
    public void checkAccess(Thread t) {}
    public void checkAccess(ThreadGroup g) {}
    public void checkExit(int status) {}
    public void checkExec(String cmd) {}
    public void checkLink(String lib) {}
    public void checkRead(FileDescriptor fd) {}
    public void checkRead(String file) {}
    public void checkRead(String file, Object context) {}
    public void checkWrite(FileDescriptor fd) {}
    public void checkWrite(String file) {}
    public void checkDelete(String file) {}
    public void checkConnect(String host, int port) {}
    public void checkConnect(String host, int port, Object context) {}
    public void checkListen(int port) {}
    public void checkAccept(String host, int port) {}
    public void checkMulticast(InetAddress maddr) {}
    public void checkPropertiesAccess() {}
    public void checkPropertyAccess(String key) {}
    public boolean checkTopLevelWindow(Object window) {
      return true;
    }
    public void checkPrintJobAccess() {}
    public void checkSystemClipboardAccess() {}
    public void checkAwtEventQueueAccess() {}
    public void checkPackageAccess(String pkg) {}
    public void checkPackageDefinition(String pkg) {}
    public void checkSetFactory() {}
    public void checkMemberAccess(Class<?> clazz, int which) {}
    public void checkSecurityAccess(String target) {}
  }
}

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


Comments
http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/7d766d2565d6
14-12-2015

Looks good to me.
11-12-2015

+1
11-12-2015

http://cr.openjdk.java.net/~kcr/8144680/webrev.00/ In JavaFX we have a couple places where we need to do a security permission check using an AccessControlContext captured at a time when the application code was on the stack. In these cases, we do the security check roughly as follows: SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (acc == null) throw ... acc.checkPermission(perm); } The fix is to replace the above pattern with the following instead: SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (acc == null) throw ... sm.checkPermission(perm, acc); } The two patterns are equivalent when using the default SecurityManager. The former will ignore (bypass) a custom security manager and is the cause of this bug. The latter is the preferred pattern for doing security checks and matches what we do in most places. The new unit tests check expected behavior with no security manager, a restrictive (as far as the needed FX permissions are concerned) security manager, and a fully permissive security manager. Without the fix for this bug, the permissive tests fail. With the fix for the bug all tests pass.
11-12-2015

Note that this is not limited to JFXPanel applications. I can also reproduce this behavior with a pure JavaFX application, which I will attach as another test case. The reason for this behavior is that we call AccessControlContext.checkPermission(Permission) rather than SecurityManager.checkPermission(Permission, AccessControlContext) in a few places, including the check for permissions to set the alwaysOnTop property. This means it will not use your security manager (whereas if you installed a custom ProtectionDomain, it would work for you). I will evaluate further as to whether or not this is a bug. If so, the fix might be as simple as changing the few calls that need the ACC to use SecurityManager.checkPermission(Permission, AccessControlContext) instead. Only somewhat related to this, JavaFX does not have fine-grained permissions in JDK 8, so we require AllPermissions. In JDK 9 we are adding fine-grained permissions -- see JDK-8091308. By itself, that won't solve the problem described in this bug, but is something to be aware of.
04-12-2015