United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6552803 the moveToFront causes Heavyweight components to be removed.
JDK-6552803 : the moveToFront causes Heavyweight components to be removed.

Details
Type:
Bug
Submit Date:
2007-05-02
Status:
Closed
Updated Date:
2011-03-07
Project Name:
JDK
Resolved Date:
2011-03-07
Component:
client-libs
OS:
windows_xp
Sub-Component:
java.awt
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
6,6u12
Fixed Versions:

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
jre 1.6 b105

ADDITIONAL OS VERSION INFORMATION :
Windows XP SP2

A DESCRIPTION OF THE PROBLEM :
When selecting a JInternalFrame with a canvas using jni and the canvas is in a JPanel the moveToFront casues a componentZorder call which removes the Canvas and causes an invalid window handle that causes the jni code to break.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Actually someone on the Sun developer team developed a simple test example to show the exception trace. The example is on the java.net site message forum:

http://forums.java.net/jive/thread.jspa?threadID=25806&tstart=0


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should not cause movie that is in canvas to disappear.
ACTUAL -
Movie is unloaded from memory

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Message in JNI code indicating WM_DESTROY and invalid window handle due to canvas peer being removed.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
Actually someone on the Sun developer team develioped a simple test example to show the exception trace. The example is on the java.net site message forum:

http://forums.java.net/jive/thread.jspa?threadID=25806&tstart=0

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

                                    

Comments
EVALUATION

We need to improve setComponentZOrder() not not remove peer in the described situation.
This will make lw&hw mixing even closer :)
                                     
2007-05-03
EVALUATION

The easiest way to get the expected results is to modify the isRemoveNotifyNeeded() method in order to allow skipping of removeNotify() for any LW component. The current logic is:

        if (comp.isLightweight()) {
            if (comp instanceof Container) {
                return ((Container)comp).hasHeavyweightDescendants();
            } else {
                return false;
            }
        }

The line that returns the result of the hasHWDesc() method could simply be modified to return false in any case. Nothing bad was observed when using this approach. Yet.

Though we might want to extend the logic in order to handle LW container containing HW children just the same way as regular HW components are handled.
                                     
2007-07-27
EVALUATION

The suggested fix successfully fixes the problem. Actually there's only one case left: the peer gets destroyed when the user minimizes the JInternalFrame. I'm in doubt whether this case should be fixed with this CR or be fixed at all.
                                     
2007-08-01
SUGGESTED FIX

$ sccs diffs -C Container.java

------- Container.java -------
*** /tmp/sccs.Vo4fHN    2007-08-01 17:37:48.000000000 +0400
--- Container.java      2007-08-01 17:30:20.000000000 +0400
***************
*** 622,649 ****
          // If component is lightweight non-Container or lightweight Container with all but heavyweight
          // children there is no need to call remove notify
          if (comp.isLightweight()) {
!             if (comp instanceof Container) {
!                 // If it has heavyweight children then removeNotify is required
!                 return ((Container)comp).hasHeavyweightDescendants();
!             } else {
!                 // Just a lightweight
                  return false;
              }
          }
  
          // All three components have peers, check for peer change
          Container newNativeContainer = oldContainer.getHeavyweightContainer();
          Container oldNativeContainer = newContainer.getHeavyweightContainer();
          if (newNativeContainer != oldNativeContainer) {
              // Native containers change - check whether or not current platform supports
              // changing of widget hierarchy on native level without recreation.
              return !comp.peer.isReparentSupported();
          } else {
              // if container didn't change we still might need to recreate component's window as
              // changes to zorder should be reflected in native window stacking order and it might
              // not be supported by the platform. This is important only for heavyweight child
!             return !comp.isLightweight() && 
!                 !((ContainerPeer)(newNativeContainer.peer)).isRestackSupported();
          }
      }
  
--- 622,650 ----
          // If component is lightweight non-Container or lightweight Container with all but heavyweight
          // children there is no need to call remove notify
          if (comp.isLightweight()) {
!             boolean isContainer = comp instanceof Container;
! 
!             if (!isContainer || (isContainer && !((Container)comp).hasHeavyweightDescendants())) {
                  return false;
              }
          }
  
+         // If this point is reached, then the comp is either a HW or a LW container with HW descendants.
+ 
          // All three components have peers, check for peer change
          Container newNativeContainer = oldContainer.getHeavyweightContainer();
          Container oldNativeContainer = newContainer.getHeavyweightContainer();
          if (newNativeContainer != oldNativeContainer) {
              // Native containers change - check whether or not current platform supports
              // changing of widget hierarchy on native level without recreation.
+             //TODO: it's quite hard to say if the LightweightPeer ever tells the truth here... 
+             //      We should probably query either the new or the old container, but which one?...
              return !comp.peer.isReparentSupported();
          } else {
              // if container didn't change we still might need to recreate component's window as
              // changes to zorder should be reflected in native window stacking order and it might
              // not be supported by the platform. This is important only for heavyweight child
!             return !((ContainerPeer)(newNativeContainer.peer)).isRestackSupported();
          }
      }
                                     
2007-08-01
EVALUATION

The final idea of the fix is as follows. setComponentZOrder() actually verifies the return value of the isRemoveNotifyNeeded(Component comp, Container oldContainer, Container newContainer) to check whether it's necessary to remove the peer of the component. Thefore it is the isRemoveNotifyNeeded() method that should be fixed.

Our implementation of the method does some common checks:
1. If the oldContainer is null or the component has no peer curretnly, there's no need to remove the peer.
2. If the newContainer doesn't have a peer, we need to remove the peer from the component.

As the second step this method separates handling of LW and HW components.

1. If the component is LW:
a) If it's a Container, then return the result of the hasHeavyweightDescendants() method. That means that the peer gets destroyed if the LW container has any HW descendants.
b) Otherwise, simply return flase. The peer should not be destroyed if this is a "simple" LW.

2. If the component is HW:
At first, we retrieve the nearest heavyweight containers of both the oldContainer and the newContainer.
a) If the nearest HW containers are the same, we return the result of the !newHWContainer.isRestackSupported(). I.e. if the 'restack' operation is supported, there's no need to remove the peer.
b) If the HW containers differ: we return the result of the !comp.peer.isReparentSupported(), i.e. we check whether the component may safely be reparented.

The problem described at this CR actually happens at the step 1. In other words, we should handle a LW container having HW descendants just the same way as a regular HW component (just like, say, the HW/LW mixing code assumes, see CR 4811096 for more information).

The proposed fix changes the handling at the second step in the following way:
1. If the component is LW:
If it's not a Container or contains no HW descendants, the method returns false, meaning there's no need to destroy the peer. Otherwise proceed to the step 2.

2. If this point is reached, the component is either a regular HW, or a LW container with HW descendants. The handling of this case is performed in the old way. 

This successfully fixes the problem described at the Description. However, there's one concern left: we cannot be sure that it's correct to query the peer of a LW component about whether the "reparent" operation is supported as it happens at step 2-b). It seems that the support of the "reparent" operation should solely be the responsibility of the container (either the old one, or the new one, or even both). Only the operation itself should slightly depend on the kind of the component (we should either reparent the component itself if it's a HW, or should recursevely reparent all the HW descendants of the LW container we're currently reparenting).
                                     
2007-08-07



Hardware and Software, Engineered to Work Together