---------- Forwarded message ----------
From: Leonardo Uribe <lu4242@gmail.com>
Date: Sat, Sep 12, 2009 at 11:03 PM
Subject: [jsr-314-open] h:outputScript target="head" is rendered in body when partial state saving is used and there is a postback
To: jsr-314-comments@jcp.org

Hi

I have notice a failure when <h:outputScript target="head" ...> is used and there is a postback, that requires change something in the spec.

Suppose the following scenario

<h:head>
</h:head>
<h:body>
    <h:outputScript name="jsf.js" library="javax.faces" target="head"/>
    <!-- some other stuff -->
</h:body>

The renderer documentation of h:outputScript and h:outputStylesheet says this:

"....The implementation of this renderer must have a @ListenerFor annotation attached to it, at the class level, declaring PostAddToViewEventclass as the value of the systemEventClass attribute. The presence of this annotation on a renderer implies the renderer implements ComponentSystemEventListener, which this renderer must do. The implementation of processEvent() must extract the UIComponent from the argument event and look for the presence of the key "target" in the component's attribute Map. If and only if such a key is present, the implementation of processEvent() must pass the component to UIViewRoot.addComponentResource()...."

Now imagine the user are submitting a form and there is a validation error. In that case there is a postback so the same view is rendered. The first request build a view like this:

<h:head>
    <h:outputScript name="jsf.js" library="javax.faces" target="head"/>
</h:head>
<h:body>
    <!-- some other stuff -->
</h:body>

but the next one restore this if partial state saving is used:

<h:head>
</h:head>
<h:body>
    <h:outputScript name="jsf.js" library="javax.faces" target="head"/>
    <!-- some other stuff -->
</h:body>

the reason why this happens is clear looking the documentation of UIComponent.getChildren():

"...After the child component has been added to the view, if the following condition is not met:
      FacesContext.isPostback() returns true and FacesContext.getCurrentPhaseId() returns PhaseId.RESTORE_VIEW..."

This is valid if we are using jsf 1.2 state saving, because we save and restore the whole tree, so the components created are already on the view. But when we use partial state saving, we do this operation:

restored view = apply delta on (fresh view from VDL.buildView() + added components after that - removed components after that)

The conclusion is:  All components that relies on ComponentSystemEventListener listening PostAddToViewEvent to be relocated in some way (h:outputScript, h:outputStylesheet, composite:insertChildren, composite:insertFacet (because we need the whole tree built before move components, otherwise nested composite components will not work correctly) ) are incompatible with partial state saving. Its more, if the affected components are moved outside/inside/between NamingContainer or UniqueIdVendor instances the state will not be correctly restored.

The solution from my point of view is relax the restriction on UIComponent.getChildren() (maybe adding a faces context attribute saying the current view is being built using partial state saving), so when partial state saving is used, PostAddToViewEvent will be published.

I'll appreciate a lot any comment.

regards

Leonardo Uribe