United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6779066 Untrusted window cannot receive input focus by clicking its taskbar button after deiconification
JDK-6779066 : Untrusted window cannot receive input focus by clicking its taskbar button after deiconification

Details
Type:
Bug
Submit Date:
2008-12-02
Status:
Resolved
Updated Date:
2011-01-19
Project Name:
JDK
Resolved Date:
2008-12-19
Component:
client-libs
OS:
solaris,generic
Sub-Component:
java.awt
CPU:
sparc,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
6u12
Fixed Versions:
6u12 (b03)

Related Reports
Duplicate:
Relates:

Sub Tasks

Description
With the fix for 6777277 there was introduced a minor side effect: if the user iconifies an untrusted Java window and then deiconifies it using the taskbar button, the window does not receive the input focus when running on Metacity window manager. Subsequent clicks on the taskbar do not give the focus either. The user has to click the Java window itself in order to give the focus.

Note: this issue is not reproducible on Compiz and KDE. It affects Gnome/Metacity only.

                                    

Comments
SUGGESTED FIX

--- old/src/solaris/classes/sun/awt/X11/XWarningWindow.java	2008-12-04 19:07:27.000000000 +0300
+++ new/src/solaris/classes/sun/awt/X11/XWarningWindow.java	2008-12-04 19:07:27.000000000 +0300
@@ -246,6 +246,54 @@
         return true;
     }
 
+    /** Send a synthetic UnmapNotify in order to withdraw the window.
+     */
+    private void withdraw() {
+        XEvent req = new XEvent();
+        try {
+            long root;
+            XToolkit.awtLock();
+            try {
+                root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
+            }
+            finally {
+                XToolkit.awtUnlock();
+            }
+
+            req.set_type(UnmapNotify);
+
+            XUnmapEvent umev = req.get_xunmap();
+
+            umev.set_event(root);
+            umev.set_window(getWindow());
+            umev.set_from_configure(false);
+
+            XToolkit.awtLock();
+            try {
+                XlibWrapper.XSendEvent(XToolkit.getDisplay(),
+                        root,
+                        false,
+                        XlibWrapper.SubstructureRedirectMask | XlibWrapper.SubstructureNotifyMask,
+                        req.pData);
+            }
+            finally {
+                XToolkit.awtUnlock();
+            }
+        } finally {
+            req.dispose();
+        }
+    }
+
+    @Override
+    protected void stateChanged(long time, int oldState, int newState) {
+        if (newState == XlibWrapper.IconicState) {
+            withdraw();
+
+            // To update the local copy of the 'visible' flag in XBaseWindow
+            super.xSetVisible(false);
+        }
+    }
+
     @Override
     protected void setMouseAbove(boolean above) {
         super.setMouseAbove(above);
@@ -271,34 +319,6 @@
         }
     }
 
-    private XAtom XA_WM_CHANGE_STATE = XAtom.get("WM_CHANGE_STATE");
-
-    private void deiconify() {
-        XClientMessageEvent req = new XClientMessageEvent();
-        try {   
-            req.set_type((int)XlibWrapper.ClientMessage);
-            req.set_window(getWindow());
-            req.set_message_type(XA_WM_CHANGE_STATE.getAtom());
-            req.set_format(32);
-            req.set_data(0, (int)XlibWrapper.NormalState);
-            req.set_data(1, 0);
-            req.set_data(2, 0);
-            XToolkit.awtLock();
-            try {
-                XlibWrapper.XSendEvent(XToolkit.getDisplay(),
-                        XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber()),
-                        false,
-                        XlibWrapper.SubstructureRedirectMask | XlibWrapper.SubstructureNotifyMask,
-                        req.pData);
-            }
-            finally {
-                XToolkit.awtUnlock();
-            }
-        } finally {
-            req.dispose();
-        }
-    }
-
     @Override
     public void xSetVisible(boolean visible) {
         super.xSetVisible(visible);
@@ -306,10 +326,6 @@
         // The _NET_WM_STATE_SKIP_TASKBAR got reset upon hiding/showing,
         // so we request it every time whenever we change the visibility.
         requestNoTaskbar();
-
-        // This is a "reverse" operation of XIconifyWindow() in case the
-        // waring window was iconified by the window manager.
-        deiconify();
     }
 
     private final Runnable hidingTask = new Runnable() {
--- old/src/solaris/classes/sun/awt/X11/XWindow.java	2008-12-04 19:07:27.000000000 +0300
+++ new/src/solaris/classes/sun/awt/X11/XWindow.java	2008-12-04 19:07:27.000000000 +0300
@@ -78,6 +78,9 @@
     protected static XAtom wm_protocols;
     protected static XAtom wm_delete_window; 
     protected static XAtom wm_take_focus;
+
+    private boolean stateChanged; // Indicates whether the value on savedState is valid
+    private int savedState; // Holds last known state of the top-level window
     
     XWindowAttributesData winAttr;
 
@@ -204,6 +207,7 @@
             XToolkit.awtUnlock();
         }
         winAttr = new XWindowAttributesData();
+        savedState = WithdrawnState;
     }
 
     void postInit(XCreateWindowParams params) {
@@ -1139,6 +1143,55 @@
         
     }
 
+    /*
+     * XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
+     * unreliable, since mapping changes can happen for a virtual desktop
+     * switch or MacOS style shading that became quite popular under X as
+     * well.  Yes, it probably should not be this way, as it violates
+     * ICCCM, but reality is that quite a lot of window managers abuse
+     * mapping state.
+     */
+    int getWMState() {
+        if (stateChanged) {
+            stateChanged = false;
+            WindowPropertyGetter getter = 
+                new WindowPropertyGetter(window, XWM.XA_WM_STATE, 0, 1, false, 
+                                         XWM.XA_WM_STATE);
+            try {
+                int status = getter.execute();
+                if (status != XlibWrapper.Success || getter.getData() == 0) {
+                    return savedState = XlibWrapper.WithdrawnState;
+                }
+            
+                if (getter.getActualType() != XWM.XA_WM_STATE.getAtom() && getter.getActualFormat() != 32) {
+                    return savedState = XlibWrapper.WithdrawnState;
+                }
+                savedState = (int)Native.getCard32(getter.getData());
+            } finally {
+                getter.dispose();
+            }
+        }
+        return savedState;
+    }
+
+    /**
+     * Override this methods to get notifications when top-level window state changes. The state is
+     * meant in terms of ICCCM: WithdrawnState, IconicState, NormalState
+     */
+    protected void stateChanged(long time, int oldState, int newState) {
+    }
+
+    @Override
+    public void handlePropertyNotify(XEvent xev) {
+        super.handlePropertyNotify(xev);
+        XPropertyEvent ev = xev.get_xproperty(); 
+        if (ev.get_atom() == XWM.XA_WM_STATE.getAtom()) {
+            // State has changed, invalidate saved value
+            stateChanged = true;
+            stateChanged(ev.get_time(), savedState, getWMState());
+        }
+    } 
+
     public void reshape(Rectangle bounds) {
         reshape(bounds.x, bounds.y, bounds.width, bounds.height);
     }
--- old/src/solaris/classes/sun/awt/X11/XWindowPeer.java	2008-12-04 19:07:27.000000000 +0300
+++ new/src/solaris/classes/sun/awt/X11/XWindowPeer.java	2008-12-04 19:07:27.000000000 +0300
@@ -52,8 +52,6 @@
     protected XWindowPeer prevTransientFor, nextTransientFor;
     private boolean grab = false; // Whether to do a grab during showing
     
-    private boolean stateChanged; // Indicates whether the value on savedState is valid
-    private int savedState; // Holds last known state of the top-level window
     private boolean mustControlStackPosition = false; // Am override-redirect not on top
     private XEventDispatcher rootPropertyEventDispatcher = null;
     private boolean firstMapped = true; // Is the top-level mapped (shown) for the first time.
@@ -99,7 +97,6 @@
         eventMask |= VisibilityChangeMask;
         params.put(EVENT_MASK, eventMask);
 
-        savedState = WithdrawnState;
         XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE");
 
         windows.add(this);
@@ -1073,11 +1070,7 @@
     public void handlePropertyNotify(XEvent xev) {
         super.handlePropertyNotify(xev);
         XPropertyEvent ev = xev.get_xproperty(); 
-	if (ev.get_atom() == XWM.XA_WM_STATE.getAtom()) {
-            // State has changed, invalidate saved value
-            stateChanged = true;
-            stateChanged(ev.get_time(), savedState, getWMState());
-        } else if (ev.get_atom() == XAtom.get("_KDE_NET_WM_FRAME_STRUT").getAtom() || ev.get_atom() == XAtom.get("_NET_WM_EXTENTS").getAtom()) {
+        if (ev.get_atom() == XAtom.get("_KDE_NET_WM_FRAME_STRUT").getAtom() || ev.get_atom() == XAtom.get("_NET_WM_EXTENTS").getAtom()) {
             getWMSetInsets(XAtom.get(ev.get_atom()));
         }
     } 
@@ -1120,6 +1113,7 @@
      * Override this methods to get notifications when top-level window state changes. The state is
      * meant in terms of ICCCM: WithdrawnState, IconicState, NormalState
      */
+    @Override
     protected void stateChanged(long time, int oldState, int newState) {
         // Fix for 6401700, 6412803
         // If this window is modal blocked, it is put into the transient_for
@@ -1137,38 +1131,6 @@
         updateSecurityWarningVisibility();
     }
 
-
-    /*
-     * XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
-     * unreliable, since mapping changes can happen for a virtual desktop
-     * switch or MacOS style shading that became quite popular under X as
-     * well.  Yes, it probably should not be this way, as it violates
-     * ICCCM, but reality is that quite a lot of window managers abuse
-     * mapping state.
-     */
-    int getWMState() {
-        if (stateChanged) {
-            stateChanged = false;
-            WindowPropertyGetter getter = 
-                new WindowPropertyGetter(window, XWM.XA_WM_STATE, 0, 1, false, 
-                                         XWM.XA_WM_STATE);
-            try {
-                int status = getter.execute();
-                if (status != XlibWrapper.Success || getter.getData() == 0) {
-                    return savedState = XlibWrapper.WithdrawnState;
-                }
-            
-                if (getter.getActualType() != XWM.XA_WM_STATE.getAtom() && getter.getActualFormat() != 32) {
-                    return savedState = XlibWrapper.WithdrawnState;
-                }
-                savedState = (int)Native.getCard32(getter.getData());
-            } finally {
-                getter.dispose();
-            }
-        }
-        return savedState;
-    }
-
     boolean isWithdrawn() {
         return getWMState() == XlibWrapper.WithdrawnState;
     }
                                     
2008-12-04
EVALUATION

This effect was observed due to special processing of the transient windows by the window manager. Since the security warning is a trasnsient window for the main application window, it got iconified upon iconifying the main window. And Metacity ignored any subsequent unmap requests for the security window. When the user restored the window, the window manager automatically showed the security warning and tried to give it the input focus. Since the window is unfocusable, the window manager left the input focus on whatever window was active before deiconification process started.

To resolve the issue we have to handle the state change property change notification and when the security warning window gets iconified we must send a synthetoc UnmapNotify for that window in order to turn it into Withdrawn state manually. This restores the reasonable behavior of the security warning.
                                     
2008-12-04



Hardware and Software, Engineered to Work Together