diff --git a/impl/src/main/java/org/glassfish/mojarra/application/view/FaceletViewHandlingStrategy.java b/impl/src/main/java/org/glassfish/mojarra/application/view/FaceletViewHandlingStrategy.java index 0384c5f9b3..c31262e1bf 100644 --- a/impl/src/main/java/org/glassfish/mojarra/application/view/FaceletViewHandlingStrategy.java +++ b/impl/src/main/java/org/glassfish/mojarra/application/view/FaceletViewHandlingStrategy.java @@ -87,6 +87,7 @@ import jakarta.faces.component.Doctype; import jakarta.faces.component.EditableValueHolder; import jakarta.faces.component.UIComponent; +import jakarta.faces.component.UIForm; import jakarta.faces.component.UIPanel; import jakarta.faces.component.UIViewRoot; import jakarta.faces.component.html.HtmlDoctype; @@ -405,7 +406,7 @@ public void renderView(FacesContext ctx, UIViewRoot viewToRender) throws IOExcep * * Note if you flag a view as transient then we won't acquire the session as you are stating it does not need one. */ - if (isServerStateSaving() && !viewToRender.isTransient()) { + if (!viewToRender.isTransient() && extContext.getSession(false) == null && isServerStateSaving() && hasForm(ctx, viewToRender)) { getSession(ctx); } @@ -1841,6 +1842,25 @@ private HttpSession getSession(FacesContext context) { return null; } + /** + * @return true if the view contains at least one {@link UIForm}, in which case state will be written and a session + * is needed under server-side state saving. + */ + private static boolean hasForm(FacesContext context, UIViewRoot viewRoot) { + if (viewRoot == null || viewRoot.getChildCount() == 0) { + return false; + } + boolean[] found = { false }; + viewRoot.visitTree(VisitContext.createVisitContext(context), (visitContext, target) -> { + if (target instanceof UIForm) { + found[0] = true; + return VisitResult.COMPLETE; + } + return VisitResult.ACCEPT; + }); + return found[0]; + } + /** * Gets and if needed initializes the faceletFactory * diff --git a/impl/src/main/java/org/glassfish/mojarra/context/PartialViewContextImpl.java b/impl/src/main/java/org/glassfish/mojarra/context/PartialViewContextImpl.java index 8ace064fa2..dc5c22a78d 100644 --- a/impl/src/main/java/org/glassfish/mojarra/context/PartialViewContextImpl.java +++ b/impl/src/main/java/org/glassfish/mojarra/context/PartialViewContextImpl.java @@ -307,6 +307,7 @@ public void processPartial(PhaseId phaseId) { if (isRenderAll()) { renderAll(ctx, viewRoot); renderState(ctx); + renderEvalScripts(ctx); doFlashPostPhaseActions(ctx); writer.endDocument(); return; diff --git a/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/CommandLinkRenderer.java b/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/CommandLinkRenderer.java index f0248cdf2f..c373ac0094 100644 --- a/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/CommandLinkRenderer.java +++ b/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/CommandLinkRenderer.java @@ -121,6 +121,15 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce writer.endElement("span"); } else { writer.endElement("a"); + + String target = (String) component.getAttributes().get("target"); + if (target != null) { + target = target.trim(); + } else { + target = ""; + } + Collection params = getBehaviorParameters(component); + RenderKitUtils.renderOnclickEventListener(context, component, params, target, true); } } @@ -163,16 +172,6 @@ protected void renderAsActive(FacesContext context, UIComponent command) throws // render the current value as link text. writeValue(command, writer); writer.flush(); - - String target = (String) command.getAttributes().get("target"); - if (target != null) { - target = target.trim(); - } else { - target = ""; - } - - Collection params = getBehaviorParameters(command); - RenderKitUtils.renderOnclickEventListener(context, command, params, target, true); } // --------------------------------------------------------- Private Methods diff --git a/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/HtmlBasicRenderer.java b/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/HtmlBasicRenderer.java index 44a8d46cc1..ec63f9be2e 100644 --- a/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/HtmlBasicRenderer.java +++ b/impl/src/main/java/org/glassfish/mojarra/renderkit/html_basic/HtmlBasicRenderer.java @@ -43,6 +43,7 @@ import jakarta.faces.application.FacesMessage; import jakarta.faces.component.NamingContainer; +import jakarta.faces.component.UICommand; import jakarta.faces.component.UIComponent; import jakarta.faces.component.UIInput; import jakarta.faces.component.UIParameter; @@ -602,13 +603,16 @@ protected boolean shouldWriteIdAttribute(UIComponent component) { // By default we only write the id attribute if: // // - We have a non-auto-generated id, or... - // - We have client behaviors. + // - We have client behaviors, or... + // - The component is a UICommand (its CSP-style click listener is attached + // via mojarra.ael(clientId, ...) which needs the id on the rendered element). // // We assume that if client behaviors are present, they // may need access to the id (AjaxBehavior certainly does). String id; return null != (id = component.getId()) && (!id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) + || component instanceof UICommand || component instanceof ClientBehaviorHolder && !((ClientBehaviorHolder) component).getClientBehaviors().isEmpty()); } diff --git a/impl/src/main/resources/META-INF/resources/jakarta.faces/faces-uncompressed.js b/impl/src/main/resources/META-INF/resources/jakarta.faces/faces-uncompressed.js index 8a53d6e25c..a07d7fce86 100644 --- a/impl/src/main/resources/META-INF/resources/jakarta.faces/faces-uncompressed.js +++ b/impl/src/main/resources/META-INF/resources/jakarta.faces/faces-uncompressed.js @@ -117,10 +117,6 @@ if ( !( (window.faces && window.faces.specversion && window.faces.specversion >= return inputElementName in form ? form[inputElementName] : getElementByName(form,inputElementName); } - /** - * append a new pair of parameter=value to a query string - * @ignore - */ /** * return true if one of the dom elements contains @@ -355,12 +351,13 @@ if ( !( (window.faces && window.faces.specversion && window.faces.specversion >= }; /** - * Remove all the portion of code matching the script pattern from the passed string + * Remove all the portion of code matching the script pattern from the passed string, + * preserving scripts whose type is set to something other than text/javascript. * @param html a String containing a portion of html * @ignore */ const removeScripts = function removeScripts(html) { - return html.replace(SCRIPT_TAG_REGEX, function(match, content) { + return html.replace(SCRIPT_TAG_REGEX, (match) => { const type = match.match(TAG_ATTRIBUTE_TYPE_REGEX); if (!!type && type[1] !== "text/javascript") { return match; // keep non-text/javascript scripts @@ -3228,17 +3225,9 @@ mojarra.l = function l(l) { if (document.readyState === "complete") { setTimeout(l); } - else if (window.addEventListener) { - window.addEventListener("load", l, false); - } - else if (typeof window.onload === "function") { - const oldListener = window.onload; - window.onload = function() { oldListener(); l(); }; - } else { - window.onload = l; + window.addEventListener("load", l); } - }; /** diff --git a/impl/src/test/ts/spec/mojarra.test.ts b/impl/src/test/ts/spec/mojarra.test.ts index afbce518f8..5a8bfee3d1 100644 --- a/impl/src/test/ts/spec/mojarra.test.ts +++ b/impl/src/test/ts/spec/mojarra.test.ts @@ -778,7 +778,7 @@ describe("mojarra.l", () => { (moj().l as Function)(callback); - expect(spy).toHaveBeenCalledWith("load", callback, false); + expect(spy).toHaveBeenCalledWith("load", callback); spy.mockRestore(); if (originalReadyState) {