7110683: Issues with some KeyboardFocusManager method
authorant
Sat, 12 May 2012 18:50:27 +0400
changeset 12651 8c69dd24bf07
parent 12650 0006acd3a5fa
child 12652 6fddf8394164
7110683: Issues with some KeyboardFocusManager method Reviewed-by: ahgross
jdk/src/share/classes/java/awt/Component.java
jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java
jdk/src/share/classes/java/awt/KeyboardFocusManager.java
jdk/src/share/classes/java/awt/Window.java
--- a/jdk/src/share/classes/java/awt/Component.java	Sat May 12 17:46:00 2012 +0400
+++ b/jdk/src/share/classes/java/awt/Component.java	Sat May 12 18:50:27 2012 +0400
@@ -7833,7 +7833,7 @@
             if (focusLog.isLoggable(PlatformLogger.FINER)) {
                 focusLog.finer("clear global focus owner");
             }
-            KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
+            KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwnerPriv();
         }
         if (focusLog.isLoggable(PlatformLogger.FINER)) {
             focusLog.finer("returning result: " + res);
@@ -7914,7 +7914,7 @@
             if (focusLog.isLoggable(PlatformLogger.FINER)) {
                 focusLog.finer("clear global focus owner");
             }
-            KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
+            KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwnerPriv();
         }
         if (focusLog.isLoggable(PlatformLogger.FINER)) {
             focusLog.finer("returning result: " + res);
@@ -7947,21 +7947,32 @@
         if (rootAncestor != null) {
             Container rootAncestorRootAncestor =
                 rootAncestor.getFocusCycleRootAncestor();
-            KeyboardFocusManager.getCurrentKeyboardFocusManager().
-                setGlobalCurrentFocusCycleRoot(
-                                               (rootAncestorRootAncestor != null)
-                                               ? rootAncestorRootAncestor
-                                               : rootAncestor);
+
+            final Container fcr = (rootAncestorRootAncestor != null) ?
+                rootAncestorRootAncestor : rootAncestor;
+
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    KeyboardFocusManager.getCurrentKeyboardFocusManager().
+                        setGlobalCurrentFocusCycleRoot(fcr);
+                    return null;
+                }
+            });
             rootAncestor.requestFocus(CausedFocusEvent.Cause.TRAVERSAL_UP);
         } else {
-            Window window = getContainingWindow();
+            final Window window = getContainingWindow();
 
             if (window != null) {
                 Component toFocus = window.getFocusTraversalPolicy().
                     getDefaultComponent(window);
                 if (toFocus != null) {
-                    KeyboardFocusManager.getCurrentKeyboardFocusManager().
-                        setGlobalCurrentFocusCycleRoot(window);
+                    AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            KeyboardFocusManager.getCurrentKeyboardFocusManager().
+                                setGlobalCurrentFocusCycleRoot(window);
+                            return null;
+                        }
+                    });
                     toFocus.requestFocus(CausedFocusEvent.Cause.TRAVERSAL_UP);
                 }
             }
--- a/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java	Sat May 12 17:46:00 2012 +0400
+++ b/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java	Sat May 12 18:50:27 2012 +0400
@@ -116,7 +116,7 @@
         } else if (fe.getOppositeComponent() != null &&
                    doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {
         } else {
-            clearGlobalFocusOwner();
+            clearGlobalFocusOwnerPriv();
         }
     }
     private void restoreFocus(WindowEvent we) {
@@ -130,7 +130,7 @@
         {
             // do nothing, everything is done in restoreFocus()
         } else {
-            clearGlobalFocusOwner();
+            clearGlobalFocusOwnerPriv();
         }
     }
     private boolean restoreFocus(Window aWindow, Component vetoedComponent,
@@ -141,7 +141,7 @@
         if (toFocus != null && toFocus != vetoedComponent && doRestoreFocus(toFocus, vetoedComponent, false)) {
             return true;
         } else if (clearOnFailure) {
-            clearGlobalFocusOwner();
+            clearGlobalFocusOwnerPriv();
             return true;
         } else {
             return false;
@@ -164,7 +164,7 @@
             {
                 return true;
             } else if (clearOnFailure) {
-                clearGlobalFocusOwner();
+                clearGlobalFocusOwnerPriv();
                 return true;
             } else {
                 return false;
--- a/jdk/src/share/classes/java/awt/KeyboardFocusManager.java	Sat May 12 17:46:00 2012 +0400
+++ b/jdk/src/share/classes/java/awt/KeyboardFocusManager.java	Sat May 12 18:50:27 2012 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -246,15 +246,7 @@
     public static void setCurrentKeyboardFocusManager(
         KeyboardFocusManager newManager) throws SecurityException
     {
-        SecurityManager security = System.getSecurityManager();
-        if (security != null) {
-            if (replaceKeyboardFocusManagerPermission == null) {
-                replaceKeyboardFocusManagerPermission =
-                    new AWTPermission("replaceKeyboardFocusManager");
-            }
-            security.
-                checkPermission(replaceKeyboardFocusManagerPermission);
-        }
+        checkReplaceKFMPermission();
 
         KeyboardFocusManager oldManager = null;
 
@@ -399,11 +391,6 @@
     private static java.util.Map mostRecentFocusOwners = new WeakHashMap();
 
     /**
-     * Error String for initializing SecurityExceptions.
-     */
-    private static final String notPrivileged = "this KeyboardFocusManager is not installed in the current thread's context";
-
-    /**
      * We cache the permission used to verify that the calling thread is
      * permitted to access the global focus state.
      */
@@ -503,17 +490,13 @@
      * @see #setGlobalFocusOwner
      * @throws SecurityException if this KeyboardFocusManager is not the
      *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      */
     protected Component getGlobalFocusOwner() throws SecurityException {
         synchronized (KeyboardFocusManager.class) {
-            if (this == getCurrentKeyboardFocusManager()) {
-                return focusOwner;
-            } else {
-                if (focusLog.isLoggable(PlatformLogger.FINER)) {
-                    focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
-                }
-                throw new SecurityException(notPrivileged);
-            }
+            checkKFMSecurity();
+            return focusOwner;
         }
     }
 
@@ -538,15 +521,23 @@
      * @see Component#requestFocus()
      * @see Component#requestFocusInWindow()
      * @see Component#isFocusable
+     * @throws SecurityException if this KeyboardFocusManager is not the
+     *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      * @beaninfo
      *       bound: true
      */
-    protected void setGlobalFocusOwner(Component focusOwner) {
+    protected void setGlobalFocusOwner(Component focusOwner)
+        throws SecurityException
+    {
         Component oldFocusOwner = null;
         boolean shouldFire = false;
 
         if (focusOwner == null || focusOwner.isFocusable()) {
             synchronized (KeyboardFocusManager.class) {
+                checkKFMSecurity();
+
                 oldFocusOwner = getFocusOwner();
 
                 try {
@@ -584,6 +575,27 @@
     }
 
     /**
+     * Clears the focus owner at both the Java and native levels if the
+     * focus owner exists and resides in the same context as the calling thread,
+     * otherwise the method returns silently.
+     * <p>
+     * The focus owner component will receive a permanent FOCUS_LOST event.
+     * After this operation completes, the native windowing system will discard
+     * all user-generated KeyEvents until the user selects a new Component to
+     * receive focus, or a Component is given focus explicitly via a call to
+     * {@code requestFocus()}. This operation does not change the focused or
+     * active Windows.
+     *
+     * @see Component#requestFocus()
+     * @see java.awt.event.FocusEvent#FOCUS_LOST
+     */
+    public void clearFocusOwner() {
+        if (getFocusOwner() != null) {
+            clearGlobalFocusOwner();
+        }
+    }
+
+    /**
      * Clears the global focus owner at both the Java and native levels. If
      * there exists a focus owner, that Component will receive a permanent
      * FOCUS_LOST event. After this operation completes, the native windowing
@@ -591,11 +603,26 @@
      * a new Component to receive focus, or a Component is given focus
      * explicitly via a call to <code>requestFocus()</code>. This operation
      * does not change the focused or active Windows.
+     * <p>
+     * If a SecurityManager is installed, the calling thread must be granted
+     * the "replaceKeyboardFocusManager" AWTPermission. If this permission is
+     * not granted, this method will throw a SecurityException, and the current
+     * focus owner will not be cleared.
+     * <p>
+     * This method is intended to be used only by KeyboardFocusManager set as
+     * current KeyboardFocusManager for the calling thread's context. It is not
+     * for general client use.
      *
+     * @see KeyboardFocusManager#clearFocusOwner
      * @see Component#requestFocus()
      * @see java.awt.event.FocusEvent#FOCUS_LOST
+     * @throws SecurityException if the calling thread does not have
+     *         "replaceKeyboardFocusManager" permission
      */
-    public void clearGlobalFocusOwner() {
+    public void clearGlobalFocusOwner()
+        throws SecurityException
+    {
+        checkReplaceKFMPermission();
         if (!GraphicsEnvironment.isHeadless()) {
             // Toolkit must be fully initialized, otherwise
             // _clearGlobalFocusOwner will crash or throw an exception
@@ -609,6 +636,15 @@
         peer.clearGlobalFocusOwner(activeWindow);
     }
 
+    void clearGlobalFocusOwnerPriv() {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                clearGlobalFocusOwner();
+                return null;
+            }
+        });
+    }
+
     Component getNativeFocusOwner() {
         return peer.getCurrentFocusOwner();
     }
@@ -660,29 +696,21 @@
      * are equivalent unless a temporary focus change is currently in effect.
      * In such a situation, the permanent focus owner will again be the focus
      * owner when the temporary focus change ends.
-     * <p>
-     * This method will throw a SecurityException if this KeyboardFocusManager
-     * is not the current KeyboardFocusManager for the calling thread's
-     * context.
      *
      * @return the permanent focus owner
      * @see #getPermanentFocusOwner
      * @see #setGlobalPermanentFocusOwner
      * @throws SecurityException if this KeyboardFocusManager is not the
      *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      */
     protected Component getGlobalPermanentFocusOwner()
         throws SecurityException
     {
         synchronized (KeyboardFocusManager.class) {
-            if (this == getCurrentKeyboardFocusManager()) {
-                return permanentFocusOwner;
-            } else {
-                if (focusLog.isLoggable(PlatformLogger.FINER)) {
-                    focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
-                }
-                throw new SecurityException(notPrivileged);
-            }
+            checkKFMSecurity();
+            return permanentFocusOwner;
         }
     }
 
@@ -708,16 +736,23 @@
      * @see Component#requestFocus()
      * @see Component#requestFocusInWindow()
      * @see Component#isFocusable
+     * @throws SecurityException if this KeyboardFocusManager is not the
+     *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      * @beaninfo
      *       bound: true
      */
     protected void setGlobalPermanentFocusOwner(Component permanentFocusOwner)
+        throws SecurityException
     {
         Component oldPermanentFocusOwner = null;
         boolean shouldFire = false;
 
         if (permanentFocusOwner == null || permanentFocusOwner.isFocusable()) {
             synchronized (KeyboardFocusManager.class) {
+                checkKFMSecurity();
+
                 oldPermanentFocusOwner = getPermanentFocusOwner();
 
                 try {
@@ -770,27 +805,19 @@
      * Returns the focused Window, even if the calling thread is in a different
      * context than the focused Window. The focused Window is the Window that
      * is or contains the focus owner.
-     * <p>
-     * This method will throw a SecurityException if this KeyboardFocusManager
-     * is not the current KeyboardFocusManager for the calling thread's
-     * context.
      *
      * @return the focused Window
      * @see #getFocusedWindow
      * @see #setGlobalFocusedWindow
      * @throws SecurityException if this KeyboardFocusManager is not the
      *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      */
     protected Window getGlobalFocusedWindow() throws SecurityException {
         synchronized (KeyboardFocusManager.class) {
-            if (this == getCurrentKeyboardFocusManager()) {
-               return focusedWindow;
-            } else {
-                if (focusLog.isLoggable(PlatformLogger.FINER)) {
-                    focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
-                }
-                throw new SecurityException(notPrivileged);
-            }
+            checkKFMSecurity();
+            return focusedWindow;
         }
     }
 
@@ -812,15 +839,23 @@
      * @see Component#requestFocus()
      * @see Component#requestFocusInWindow()
      * @see Window#isFocusableWindow
+     * @throws SecurityException if this KeyboardFocusManager is not the
+     *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      * @beaninfo
      *       bound: true
      */
-    protected void setGlobalFocusedWindow(Window focusedWindow) {
+    protected void setGlobalFocusedWindow(Window focusedWindow)
+        throws SecurityException
+    {
         Window oldFocusedWindow = null;
         boolean shouldFire = false;
 
         if (focusedWindow == null || focusedWindow.isFocusableWindow()) {
             synchronized (KeyboardFocusManager.class) {
+                checkKFMSecurity();
+
                 oldFocusedWindow = getFocusedWindow();
 
                 try {
@@ -874,27 +909,19 @@
      * or its children with special decorations, such as a highlighted title
      * bar. The active Window is always either the focused Window, or the first
      * Frame or Dialog that is an owner of the focused Window.
-     * <p>
-     * This method will throw a SecurityException if this KeyboardFocusManager
-     * is not the current KeyboardFocusManager for the calling thread's
-     * context.
      *
      * @return the active Window
      * @see #getActiveWindow
      * @see #setGlobalActiveWindow
      * @throws SecurityException if this KeyboardFocusManager is not the
      *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      */
     protected Window getGlobalActiveWindow() throws SecurityException {
         synchronized (KeyboardFocusManager.class) {
-            if (this == getCurrentKeyboardFocusManager()) {
-               return activeWindow;
-            } else {
-                if (focusLog.isLoggable(PlatformLogger.FINER)) {
-                    focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
-                }
-                throw new SecurityException(notPrivileged);
-            }
+            checkKFMSecurity();
+            return activeWindow;
         }
     }
 
@@ -917,12 +944,20 @@
      * @see #getGlobalActiveWindow
      * @see Component#requestFocus()
      * @see Component#requestFocusInWindow()
+     * @throws SecurityException if this KeyboardFocusManager is not the
+     *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      * @beaninfo
      *       bound: true
      */
-    protected void setGlobalActiveWindow(Window activeWindow) {
+    protected void setGlobalActiveWindow(Window activeWindow)
+        throws SecurityException
+    {
         Window oldActiveWindow;
         synchronized (KeyboardFocusManager.class) {
+            checkKFMSecurity();
+
             oldActiveWindow = getActiveWindow();
             if (focusLog.isLoggable(PlatformLogger.FINER)) {
                 focusLog.finer("Setting global active window to " + activeWindow + ", old active " + oldActiveWindow);
@@ -1194,10 +1229,6 @@
      * Components represent the next and previous Components to focus during
      * normal focus traversal. In that case, the current focus cycle root is
      * used to differentiate among the possibilities.
-     * <p>
-     * This method will throw a SecurityException if this KeyboardFocusManager
-     * is not the current KeyboardFocusManager for the calling thread's
-     * context.
      *
      * @return the current focus cycle root, or null if the current focus cycle
      *         root is not a member of the calling thread's context
@@ -1205,19 +1236,15 @@
      * @see #setGlobalCurrentFocusCycleRoot
      * @throws SecurityException if this KeyboardFocusManager is not the
      *         current KeyboardFocusManager for the calling thread's context
+     *         and if the calling thread does not have "replaceKeyboardFocusManager"
+     *         permission
      */
     protected Container getGlobalCurrentFocusCycleRoot()
         throws SecurityException
     {
         synchronized (KeyboardFocusManager.class) {
-            if (this == getCurrentKeyboardFocusManager()) {
-                return currentFocusCycleRoot;
-            } else {
-                if (focusLog.isLoggable(PlatformLogger.FINER)) {
-                    focusLog.finer("This manager is " + this + ", current is " + getCurrentKeyboardFocusManager());
-                }
-                throw new SecurityException(notPrivileged);
-            }
+            checkKFMSecurity();
+            return currentFocusCycleRoot;
         }
     }
 
@@ -1228,16 +1255,27 @@
      * In that case, the current focus cycle root is used to differentiate
      * among the possibilities.
      * <p>
+     * If a SecurityManager is installed, the calling thread must be granted
+     * the "replaceKeyboardFocusManager" AWTPermission. If this permission is
+     * not granted, this method will throw a SecurityException, and the current
+     * focus cycle root will not be changed.
+     * <p>
      * This method is intended to be used only by KeyboardFocusManagers and
      * focus implementations. It is not for general client use.
      *
      * @param newFocusCycleRoot the new focus cycle root
      * @see #getCurrentFocusCycleRoot
      * @see #getGlobalCurrentFocusCycleRoot
+     * @throws SecurityException if the calling thread does not have
+     *         "replaceKeyboardFocusManager" permission
      * @beaninfo
      *       bound: true
      */
-    public void setGlobalCurrentFocusCycleRoot(Container newFocusCycleRoot) {
+    public void setGlobalCurrentFocusCycleRoot(Container newFocusCycleRoot)
+        throws SecurityException
+    {
+        checkReplaceKFMPermission();
+
         Container oldFocusCycleRoot;
 
         synchronized (KeyboardFocusManager.class) {
@@ -3058,4 +3096,39 @@
                 : null;
         }
     }
+
+    private static void checkReplaceKFMPermission()
+        throws SecurityException
+    {
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            if (replaceKeyboardFocusManagerPermission == null) {
+                replaceKeyboardFocusManagerPermission =
+                    new AWTPermission("replaceKeyboardFocusManager");
+            }
+            security.
+                checkPermission(replaceKeyboardFocusManagerPermission);
+        }
+    }
+
+    // Checks if this KeyboardFocusManager instance is the current KFM,
+    // or otherwise checks if the calling thread has "replaceKeyboardFocusManager"
+    // permission. Here's the reasoning to do so:
+    //
+    // A system KFM instance (which is the current KFM by default) may have no
+    // "replaceKFM" permission when a client code is on the call stack beneath,
+    // but still it should be able to execute the methods protected by this check
+    // due to the system KFM is trusted (and so it does like "privileged").
+    //
+    // If this KFM instance is not the current KFM but the client code has all
+    // permissions we can't throw SecurityException because it would contradict
+    // the security concepts. In this case the trusted client code is responsible
+    // for calling the secured methods from KFM instance which is not current.
+    private void checkKFMSecurity()
+        throws SecurityException
+    {
+        if (this != getCurrentKeyboardFocusManager()) {
+            checkReplaceKFMPermission();
+        }
+    }
 }
--- a/jdk/src/share/classes/java/awt/Window.java	Sat May 12 17:46:00 2012 +0400
+++ b/jdk/src/share/classes/java/awt/Window.java	Sat May 12 18:50:27 2012 +0400
@@ -2568,7 +2568,7 @@
                     }
                 }
             KeyboardFocusManager.getCurrentKeyboardFocusManager().
-                clearGlobalFocusOwner();
+                clearGlobalFocusOwnerPriv();
         }
     }