javaee / mojarra

PLEASE NOTE: This project has moved to Eclipse Foundation and will be archived under the JavaEE GitHub Organization. After Feb. 1, 2021, the new location will be github.com/javaee/mojarra. Mojarra - Oracle's implementation of the JavaServer Faces specification
https://github.com/eclipse-ee4j/mojarra
Other
165 stars 58 forks source link

Update @all inside a form lead to javascript error. #4354

Open bigbonsai opened 6 years ago

bigbonsai commented 6 years ago

Stackoverflow

Seems like the component resources are available (see PrimeFaces HeadRenderer) in the postback but somehow they are not rendered in the response inside the head tag.

NOTE: this will only happen with update=@all as otherwise the head-tag won't be replaced.

Discoverd with PrimeFaces 6.2 on Mojarra 3.3.2.

<h:form>
        <p:commandButton value="BUTTON_TEXT" update="@all"
            process="@none" />
    </h:form>

After pressing this button, this stacktraces shows the problem.

VM47:1 Uncaught TypeError: Cannot read property 'cw' of null
    at <anonymous>:1:12
    at p (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at Ja (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.append (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.<anonymous> (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at T (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.html (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.updateBody (core.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.updateElement (core.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.doUpdate (core.js.xhtml?ln=primefaces&v=6.2:3)
tandraschko commented 6 years ago

@BalusC / @arjantijms Could you have a look at it? It seems quite annoying for users.

It may be related to the dynamic resource loading feature. In the PF HeadRenderer, we get all resources and call #encodeAllon them, to render the script/style tag. This worked fire in every Mojarra and MyFaces version but it seems that Mojarra 2.3 doesn't render the tags when you are in ajax request with update=@all. The weird thing is that viewRoot#getComponentResources delivers all correct resources but #encodeAll does nothing.

claudioweiler commented 6 years ago

@BalusC I'm upgrading my app to Wildfly 15 (Mojarra 2.3.5.SP2 + Primefaces 6.2 + Omnifaces 3.2). A minimal diference to Omnifaces ShowCase is Mojarra 2.3.5.SP1 (Wildfly 13).

I'm using FullAjaxExceptionHandler and getting mentioned error when handling ajax exceptions. Could this be related?

core.js.xhtml?ln=primefaces&v=6.2:3 Uncaught TypeError: Cannot read property 'error' of null
    at Object.<anonymous> (core.js.xhtml?ln=primefaces&v=6.2:3)
    at i (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at Object.fireWith [as resolveWith] (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at A (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at XMLHttpRequest.<anonymous> (jquery.js.xhtml?ln=primefaces&v=6.2:4)
VM2128:1 Uncaught TypeError: Cannot read property 'cw' of null
    at <anonymous>:1:12
    at p (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at Ja (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.append (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.<anonymous> (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at T (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at r.fn.init.html (jquery.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.updateBody (core.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.updateElement (core.js.xhtml?ln=primefaces&v=6.2:3)
    at Object.doUpdate (core.js.xhtml?ln=primefaces&v=6.2:3)
VM2125:1 Uncaught TypeError: Cannot read property 'locales' of null
    at <anonymous>:1:12
    at p (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at Function.globalEval (jquery.js.xhtml?ln=primefaces&v=6.2:2)
    at text script (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at Qb (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at A (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at XMLHttpRequest.<anonymous> (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at Object.send (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at Function.ajax (jquery.js.xhtml?ln=primefaces&v=6.2:4)
    at Function.r._evalUrl (jquery.js.xhtml?ln=primefaces&v=6.2:4)
tandraschko commented 6 years ago

should be the same bug, yes

claudioweiler commented 6 years ago

As @tandraschko said, Mojarra is not rendering all the resources during an ajax request with update=\@ALL.

@BalusC One possible hack (in OmniFaces) so that the FullAjaxExceptionHandler works again: Edit the method Hacks.removeResourceDependencyState (used by FullAjaxExceptionHandler), clearing JSF's processed resource dependencies:

context.getAttributes().keySet().remove(javax.faces.application.ResourceHandler.RESOURCE_IDENTIFIER);

The snippet above makes JSF render all resources again. I don't know if it affects other funcionalities, as I've only tested the exception handled by FullAjaxExceptionHandler.

@tandraschko

Additionally, dealing with this issue, I've observed that PrimeFaces updates the <head> tag differently from the Mojarra and MyFaces way of doing it when it comes to updating elements in an ajax response (javascript).

PrimeFaces replaces the <head>:

$('head').html(content.substring(headStartIndex, content.lastIndexOf("</head>")));

This act of "replacing" the head tag brings some weird behaviors as the browser removes the styles and replaces them with new ones (the user perceives a "flicker" sometimes).

Mojarra do not allow replacement of the head tag, stating that

javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents. (https://github.com/javaserverfaces/mojarra/blob/master/impl/src/main/js/jsf.js)

When it comes to update ViewRoot, it replaces the <body> tag and executes the same code triggered by a change in javax.faces.Resource, appending new scripts and styles to the head (but not replacing it).

Myfaces seems to proceed this way either:

we cannot replace the head, almost no browser allows this, some of them throw errors others simply ignore it or replace it and destroy the dom that way! (https://github.com/apache/myfaces/blob/master/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js)

Myfaces' _replaceHead function bears the following comment:

replaces a current head theoretically, pratically only the scripts are evaled anew since nothing else can be changed.

Omniface's FullAjaxExceptionHandler showcase works because the demo was created using a Mojarra component (<h:commandButton... with <f:ajax...). Mojarra do not replace the head tag, and everything works as normal (despite the fact that the partial-response should contain all the resources). Link to omnifaces' showcase: http://showcase.omnifaces.org/exceptionhandlers/FullAjaxExceptionHandler

The problem becomes apparent if the demo is created with PrimeFaces (<p:commandButton ajax="true" ...), because PrimeFaces replaces the head element. Since the head provided by the partial-response is incomplete, the view is rendered in a broken way (often missing critical javascript and styles).

One work around could be to change PrimeFaces' functions PrimeFaces.ajax.Utils.updateHead and PrimeFaces.ajax.Utils.updateElement (in core.js) to behave the way Mojarra and MyFaces do. Is there any known issue with that?

Below is a javascript hack that attempts to ilustrate that.

primefaces_replacehead_hack.js.txt

tandraschko commented 6 years ago

I, personally, think that update=@all should also replace the whole head content. update=@all is used to replace the whole view (including title e.g.). The flickering is expected as the head content is replaced - also @all should only be used for special cases like FullAjaxExceptionHandler.

Maybe the PF behavior is not perfect, but the JSF behavior also is not perfect. We replace the content since years now in PrimeFaces and we didn't had any issues in modern browsers.

But back to the actual issue: I think Mojarra should behave like all older Mojarra versions and MyFaces: it should render the resource, when someone tries to manually render the resource component via UIComponent#encodeAll. This makes it more flexible for third-party-libs and compatible with older versions.

It doesn't mean that the JSF impl have to render the resources in their HeadRenderer on AJAX requests, they can skip it. But they should also allow it to render if some would like to:

This is our code: for (UIComponent resource : viewRoot.getComponentResources(context, "head")) { resource.encodeAll(context); }