Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions impl/src/main/java/com/sun/faces/facelets/tag/ui/UIDebug.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,15 @@
import java.util.List;
import java.util.Map;

import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.facelets.util.FastWriter;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.renderkit.html_basic.ScriptRenderer;

import jakarta.faces.component.UIComponentBase;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.servlet.http.HttpServletResponse;

import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.facelets.util.FastWriter;
import com.sun.faces.renderkit.RenderKitUtils;

/**
* @author Jacob Hookom
*/
Expand Down Expand Up @@ -103,14 +102,9 @@ public void encodeBegin(FacesContext facesContext) throws IOException {
ResponseWriter writer = facesContext.getResponseWriter();
writer.startElement("span", this);
writer.writeAttribute("id", getClientId(facesContext), "id");
writer.startElement("script", this);

if (!RenderKitUtils.isOutputHtml5Doctype(facesContext)) {
writer.writeAttribute("type", ScriptRenderer.DEFAULT_CONTENT_TYPE, "type");
}

RenderKitUtils.renderScript(facesContext, this, null, sb.toString());

writer.writeText(sb.toString(), this, null);
writer.endElement("script");
writer.endElement("span");
}
}
Expand Down
132 changes: 105 additions & 27 deletions impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.el.ELUtils;
import com.sun.faces.facelets.util.DevTools;
import com.sun.faces.renderkit.html_basic.ScriptRenderer;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.RequestStateManager;
import com.sun.faces.util.Util;
Expand Down Expand Up @@ -143,6 +144,8 @@ public class RenderKitUtils {
*/
private static final String ATTRIBUTES_THAT_ARE_SET_KEY = UIComponentBase.class.getName() + ".attributesThatAreSet";

private static final String BEHAVIOR_EVENT_ATTRIBUTE_PREFIX = "on";

/**
* UIViewRoot attribute key of a boolean value which remembers whether the view will be rendered with a HTML5 doctype.
*/
Expand Down Expand Up @@ -347,10 +350,10 @@ public static void renderPassThruAttributes(FacesContext context, ResponseWriter
}
}

// Renders the onchange handler for input components. Handles
// Renders the onchange event listener for input components. Handles
// chaining together the user-provided onchange handler with
// any Behavior scripts.
public static void renderOnchange(FacesContext context, UIComponent component, boolean incExec) throws IOException {
public static void renderOnchangeEventListener(FacesContext context, UIComponent component, boolean incExec) throws IOException {

final String handlerName = "onchange";
final Object userHandler = component.getAttributes().get(handlerName);
Expand All @@ -369,11 +372,11 @@ public static void renderOnchange(FacesContext context, UIComponent component, b
params = new LinkedList<>();
params.add(new ClientBehaviorContext.Parameter("incExec", true));
}
renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec);
renderHandler(context, component, null, params, handlerName, userHandler, behaviorEventName, "change", null, false, incExec, true);
}

// Renders onclick handler for SelectRaidio and SelectCheckbox
public static void renderSelectOnclick(FacesContext context, UIComponent component, boolean incExec) throws IOException {
// Renders onclick event listener for SelectRadio and SelectCheckbox
public static void renderSelectOnclickEventListener(FacesContext context, UIComponent component, String clientId, boolean incExec) throws IOException {

final String handlerName = "onclick";
final Object userHandler = component.getAttributes().get(handlerName);
Expand All @@ -392,14 +395,16 @@ public static void renderSelectOnclick(FacesContext context, UIComponent compone
params = new LinkedList<>();
params.add(new ClientBehaviorContext.Parameter("incExec", true));
}
renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec);
renderHandler(context, component, clientId, params, handlerName, userHandler, behaviorEventName, "click", null, false, incExec, true);
}

// Renders the onclick handler for command buttons. Handles
// Renders the onclick event listener for command buttons. Handles
// chaining together the user-provided onclick handler, any
// Behavior scripts, plus the default button submit script.
public static void renderOnclick(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String submitTarget,
boolean needsSubmit) throws IOException {
public static void renderOnclickEventListener(FacesContext context, UIComponent component,
Collection<ClientBehaviorContext.Parameter> params,
String submitTarget,
boolean needsSubmit) throws IOException {

final String handlerName = "onclick";
final Object userHandler = component.getAttributes().get(handlerName);
Expand All @@ -418,18 +423,17 @@ public static void renderOnclick(FacesContext context, UIComponent component, Co
}
}

renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, submitTarget, needsSubmit, false);
renderHandler(context, component, null, params, handlerName, userHandler, behaviorEventName, "click", submitTarget, needsSubmit, false, true);
}

// Renders the script function for command scripts.
// Renders the script element with the function for command scripts.
public static void renderFunction(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String submitTarget)
throws IOException {

ClientBehaviorContext behaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, component, "action", submitTarget, params);
AjaxBehavior behavior = (AjaxBehavior) context.getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
mapAttributes(component, behavior, "execute", "render", "onerror", "onevent", "resetValues");

context.getResponseWriter().append(behavior.getScript(behaviorContext));
renderScript(context, component, null, behavior.getScript(behaviorContext));
}

private static void mapAttributes(UIComponent component, AjaxBehavior behavior, String... attributeNames) {
Expand Down Expand Up @@ -623,7 +627,19 @@ private static void renderPassThruAttributesOptimized(FacesContext context, Resp
Attribute attr = knownAttributes[index];

if (isBehaviorEventAttribute(attr, behaviorEventName)) {
renderHandler(context, component, null, name, value, behaviorEventName, null, false, false);
renderHandler(context, component, null, null, name, value, behaviorEventName, behaviorEventName, null, false, false, false);

renderedBehavior = true;
} else {
writer.writeAttribute(prefixAttribute(name, isXhtml), value, name);
}
}
}
else if (isBehaviorEventAttribute(name)) {
Object value = attrMap.get(name);
if (value != null && shouldRenderAttribute(value)) {
if (name.substring(2).equals(behaviorEventName)) {
renderHandler(context, component, null, null, name, value, behaviorEventName, behaviorEventName, null, false, false, false);

renderedBehavior = true;
} else {
Expand All @@ -644,8 +660,8 @@ private static void renderPassThruAttributesOptimized(FacesContext context, Resp
Attribute attr = knownAttributes[i];
String[] events = attr.getEvents();
if (events != null && events.length > 0 && behaviorEventName.equals(events[0])) {

renderHandler(context, component, null, attr.getName(), null, behaviorEventName, null, false, false);
renderHandler(context, component, null, null, attr.getName(), null, behaviorEventName, behaviorEventName, null, false, false, false);
return;
}
}

Expand Down Expand Up @@ -683,12 +699,34 @@ private static void renderPassThruAttributesUnoptimized(FacesContext context, Re

// If we've got a behavior for this attribute,
// we may need to chain scripts together, so use
// renderHandler().
renderHandler(context, component, null, attrName, value, events[0], null, false, false);
// renderEventListener().
renderHandler(context, component, null, null, attrName, value, events[0], events[0], null, false, false, false);
}
}
}

private static void renderPassthruAttribute(FacesContext context, ResponseWriter writer, UIComponent component,
Map<String, List<ClientBehavior>> behaviors, boolean isXhtml, Map<String, Object> attrMap, String attrName,
String eventName) throws IOException {
boolean hasBehavior = eventName != null && behaviors.containsKey(eventName);

Object value = attrMap.get(attrName);

if (value != null && shouldRenderAttribute(value) && !hasBehavior) {
writer.writeAttribute(prefixAttribute(attrName, isXhtml), value, attrName);
} else if (hasBehavior) {

// If we've got a behavior for this attribute,
// we may need to chain scripts together, so use
// renderEventListener().
renderHandler(context, component, null, null, attrName, value, eventName, eventName, null, false, false, false);
}
}

public static boolean isBehaviorEventAttribute(String name) {
return name.startsWith(BEHAVIOR_EVENT_ATTRIBUTE_PREFIX) && name.length() > 2;
}

/**
* <p>
* Determines if an attribute should be rendered based on the specified #attributeVal.
Expand Down Expand Up @@ -1492,7 +1530,7 @@ private static String getSubmitHandler(FacesContext context, UIComponent compone
builder.append("')");

if (preventDefault) {
builder.append(";return false");
builder.append(";event.preventDefault()");
}

return builder.toString();
Expand Down Expand Up @@ -1530,10 +1568,10 @@ private static String getChainedHandler(FacesContext context, UIComponent compon
builder.append(")");

// If we're submitting (either via a behavior, or by rendering
// a submit script), we need to return false to prevent the
// default button/link action.
// a submit script), we need to prevent the
// default button/link action event.
if (submitting && ("action".equals(behaviorEventName) || "click".equals(behaviorEventName))) {
builder.append(";return false");
builder.append(";event.preventDefault()");
}

return builder.toString();
Expand All @@ -1554,7 +1592,7 @@ private static String getSingleBehaviorHandler(FacesContext context, UIComponent
script = getSubmitHandler(context, component, params, submitTarget, preventDefault);
}
} else if (preventDefault) {
script = script + ";return false";
script = script + ";event.preventDefault()";
}

return script;
Expand Down Expand Up @@ -1583,15 +1621,16 @@ private static boolean isSubmitting(ClientBehavior behavior) {
* @param handlerValue the user-specified value for the handler attribute
* @param behaviorEventName the name of the behavior event that corresponds to this handler (eg. "action" or
* "mouseover").
* @param domEventName the name of the DOM event that corresponds to this handler (eg. "click" or
* "change").
* @param needsSubmit indicates whether the mojarra.cljs() "submit" script is required by the component. Most
* components do not need this, either because they submit themselves (eg. commandButton), or because they do not
* perform submits (eg. non-command components). This flag is mainly here for the commandLink case, where we need to
* render the submit script to make the link submit.
*/
private static void renderHandler(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String handlerName,
Object handlerValue, String behaviorEventName, String submitTarget, boolean needsSubmit, boolean includeExec) throws IOException {
private static void renderHandler(FacesContext context, UIComponent component, String clientId, Collection<ClientBehaviorContext.Parameter> params, String handlerName,
Object handlerValue, String behaviorEventName, String domEventName, String submitTarget, boolean needsSubmit, boolean includeExec, boolean asEventListener) throws IOException {

ResponseWriter writer = context.getResponseWriter();
String userHandler = getNonEmptyUserHandler(handlerValue);
List<ClientBehavior> behaviors = getClientBehaviors(component, behaviorEventName);

Expand Down Expand Up @@ -1625,7 +1664,46 @@ private static void renderHandler(FacesContext context, UIComponent component, C
assert false;
}

writer.writeAttribute(handlerName, handler, null);
if (handler != null) {
if (asEventListener) {
addEventListener(context, component, clientId, domEventName, handler);
}
else {
context.getResponseWriter().writeAttribute(handlerName, handler, null);
}
}
}

public static void addEventListener(FacesContext context, UIComponent component, String clientId, String domEventName, String function) throws IOException {
StringBuilder script = new StringBuilder("mojarra.ael('")
.append(clientId != null ? clientId : component.getClientId(context))
.append("','")
.append(domEventName)
.append("',function(event){" + function + "})");

if (context.getPartialViewContext().isAjaxRequest()) {
context.getPartialViewContext().getEvalScripts().add(script.toString());
}
else {
renderScript(context, component, null, script.toString());
}
}

public static void renderScript(FacesContext context, UIComponent component, String clientId, String script) throws IOException {
ResponseWriter writer = context.getResponseWriter();

writer.startElement("script", component);

if (clientId != null) {
writer.writeAttribute("id", clientId, "id");
}

if (!RenderKitUtils.isOutputHtml5Doctype(context)) {
writer.writeAttribute("type", ScriptRenderer.DEFAULT_CONTENT_TYPE, "type");
}

writer.writeText(script, component, null);
writer.endElement("script");
}

// Determines the type of handler to render based on what sorts of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,12 @@

package com.sun.faces.renderkit.html_basic;

import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.BEHAVIOR_SOURCE_PARAM;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import com.sun.faces.RIConstants;
import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;

import jakarta.faces.component.UICommand;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.behavior.ClientBehavior;
Expand All @@ -39,6 +32,11 @@
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.event.ActionEvent;

import com.sun.faces.RIConstants;
import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;

/**
* <B>ButtonRenderer</B> is a class that renders the current value of <code>UICommand</code> as a Button.
*/
Expand Down Expand Up @@ -98,11 +96,6 @@ public void encodeBegin(FacesContext context, UIComponent component) throws IOEx
* when we decide how to do script injection.
*/

Collection<ClientBehaviorContext.Parameter> params = getBehaviorParameters(component);
if (!params.isEmpty() && (type.equals("submit") || type.equals("button"))) {
RenderKitUtils.renderFacesJsIfNecessary(context);
}

String imageSrc = (String) component.getAttributes().get("image");
writer.startElement("input", component);
writeIdAttributeIfNecessary(context, writer, component);
Expand Down Expand Up @@ -134,8 +127,6 @@ else if (writer.getContentType().equals(RIConstants.XHTML_CONTENT_TYPE)) {
writer.writeAttribute("class", styleClass, "styleClass");
}

RenderKitUtils.renderOnclick(context, component, params, null, false);

// PENDING(edburns): Prior to i_spec_1111, this element
// was rendered unconditionally

Expand All @@ -155,6 +146,15 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
if (component.getChildCount() > 0) {
context.getResponseWriter().endElement("input");
}

String type = getButtonType(component);
Collection<ClientBehaviorContext.Parameter> params = getBehaviorParameters(component);

if (!params.isEmpty() && (type.equals("submit") || type.equals("button"))) {
RenderKitUtils.renderFacesJsIfNecessary(context);
}

RenderKitUtils.renderOnclickEventListener(context, component, params, null, false);
}

// --------------------------------------------------------- Private Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
import java.util.Map;
import java.util.logging.Level;

import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;

import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.convert.ConverterException;

import com.sun.faces.renderkit.Attribute;
import com.sun.faces.renderkit.AttributeManager;
import com.sun.faces.renderkit.RenderKitUtils;

/**
* <B>CheckboxRenderer</B> is a class that renders the current value of <code>UISelectBoolean</code> as a checkbox.
*/
Expand Down Expand Up @@ -104,10 +104,10 @@ protected void getEndTextToRender(FacesContext context, UIComponent component, S
RenderKitUtils.renderPassThruAttributes(context, writer, component, ATTRIBUTES, getNonOnClickSelectBehaviors(component));
RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);

RenderKitUtils.renderSelectOnclick(context, component, false);

writer.endElement("input");

RenderKitUtils.renderSelectOnclickEventListener(context, component, null, false);

}

// --------------------------------------------------------- Private Methods
Expand Down
Loading
Loading